swarph-cli 0.69.8__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 (455) hide show
  1. {swarph_cli-0.69.8/src/swarph_cli.egg-info → swarph_cli-0.70.0}/PKG-INFO +1 -1
  2. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/pyproject.toml +1 -1
  3. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/mesh.py +172 -30
  5. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/monitor.py +44 -7
  6. swarph_cli-0.70.0/src/swarph_cli/scripts/wake_watchdog.py +385 -0
  7. {swarph_cli-0.69.8 → swarph_cli-0.70.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  8. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
  9. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_644_monitor_supervision.py +107 -0
  10. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_729_wake_watchdog.py +2 -0
  11. swarph_cli-0.70.0/tests/test_739_wake_writer.py +479 -0
  12. swarph_cli-0.70.0/tests/test_961_cursor_grok_footer.py +331 -0
  13. swarph_cli-0.69.8/src/swarph_cli/scripts/wake_watchdog.py +0 -209
  14. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/LICENSE +0 -0
  15. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/README.md +0 -0
  16. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/setup.cfg +0 -0
  17. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/__main__.py +0 -0
  18. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/__init__.py +0 -0
  19. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/backends.py +0 -0
  20. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/claude_cli.py +0 -0
  21. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/pack.py +0 -0
  22. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/prices.py +0 -0
  23. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/providers.py +0 -0
  24. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/quality.py +0 -0
  25. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  26. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/rules.py +0 -0
  27. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/runner.py +0 -0
  28. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/bench/validate.py +0 -0
  29. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/caller.py +0 -0
  30. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/__init__.py +0 -0
  31. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/harden.py +0 -0
  32. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/lineage.py +0 -0
  33. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/liveness.py +0 -0
  34. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/manifest.py +0 -0
  35. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/paths.py +0 -0
  36. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/capture/verify.py +0 -0
  37. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/cell.py +0 -0
  38. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/chain_token.py +0 -0
  39. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/channel.py +0 -0
  40. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/__init__.py +0 -0
  41. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/_content.py +0 -0
  42. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/_display.py +0 -0
  43. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  44. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/add.py +0 -0
  45. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/bench.py +0 -0
  46. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/board.py +0 -0
  47. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  48. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain.py +0 -0
  49. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  50. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell.py +0 -0
  51. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  52. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  53. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/channel.py +0 -0
  54. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/chat.py +0 -0
  55. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph.py +0 -0
  56. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  57. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  58. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  59. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/compress.py +0 -0
  60. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/daemon.py +0 -0
  61. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/dreaming.py +0 -0
  62. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/event.py +0 -0
  63. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/gateway.py +0 -0
  64. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/gh_route.py +0 -0
  65. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/group.py +0 -0
  66. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/guide.py +0 -0
  67. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/highlight.py +0 -0
  68. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  69. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_output.py +0 -0
  70. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/hooks.py +0 -0
  71. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/import_session.py +0 -0
  72. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/init.py +0 -0
  73. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  74. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_hook.py +0 -0
  75. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  76. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  77. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  78. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  79. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/lane.py +0 -0
  80. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  81. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory.py +0 -0
  82. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  83. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  84. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/okf_links.py +0 -0
  85. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/onboard.py +0 -0
  86. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  87. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  88. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  89. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/ratify.py +0 -0
  90. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/schedule.py +0 -0
  91. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/security.py +0 -0
  92. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/service.py +0 -0
  93. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/spawn.py +0 -0
  94. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/timeline.py +0 -0
  95. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/version.py +0 -0
  96. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  97. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/commands/watchdog.py +0 -0
  98. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/compress/__init__.py +0 -0
  99. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/compress/levers.py +0 -0
  100. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/compress/marker.py +0 -0
  101. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/compress/verify.py +0 -0
  102. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/console_safe.py +0 -0
  103. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/delivery_queue.py +0 -0
  104. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dm_frame.py +0 -0
  105. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  106. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  107. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/clone.py +0 -0
  108. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/enrich.py +0 -0
  109. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  110. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/notify.py +0 -0
  111. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/organize.py +0 -0
  112. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/probes.py +0 -0
  113. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/report.py +0 -0
  114. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/run.py +0 -0
  115. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/slm.py +0 -0
  116. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  117. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/verify.py +0 -0
  118. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/__init__.py +0 -0
  119. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  120. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  121. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/schema.sql +0 -0
  122. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/server.py +0 -0
  123. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway/services_control.py +0 -0
  124. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gateway_default.py +0 -0
  125. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/gh_identity.py +0 -0
  126. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/guide/GUIDE.md +0 -0
  127. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/identity.py +0 -0
  128. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/main.py +0 -0
  129. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/multiplexer.py +0 -0
  130. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/orphan_daemons.py +0 -0
  131. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/pane_probe.py +0 -0
  132. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/parsers/__init__.py +0 -0
  133. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/parsers/claude.py +0 -0
  134. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  135. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  136. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  137. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  138. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  139. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  140. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  141. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  142. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  143. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  144. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  145. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  146. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor.py +0 -0
  147. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  148. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  149. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  150. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_host.py +0 -0
  151. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_payload.py +0 -0
  152. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_reply.py +0 -0
  153. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/peer_staging.py +0 -0
  154. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/query_filters.py +0 -0
  155. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/scripts/__init__.py +0 -0
  156. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  157. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  158. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  159. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  160. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/service/__init__.py +0 -0
  161. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/service/app.py +0 -0
  162. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/service/providers.py +0 -0
  163. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/session_bridge.py +0 -0
  164. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  165. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  166. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/source_text.py +0 -0
  167. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/stall_alert.py +0 -0
  168. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  169. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  170. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  171. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  172. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  173. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  174. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  175. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  176. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  177. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  178. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  179. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  180. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  181. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  182. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/timeline_paths.py +0 -0
  183. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli/tokens.py +0 -0
  184. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  185. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  186. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  187. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  188. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_1006_session_board.py +0 -0
  189. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_124_register_merge.py +0 -0
  190. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_126_pending_from_complete.py +0 -0
  191. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  192. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_138_memory_navigate.py +0 -0
  193. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_139_tool_probe.py +0 -0
  194. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_183_card_anchor.py +0 -0
  195. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_184_notify_honesty.py +0 -0
  196. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_201_win32_bash_refusal.py +0 -0
  197. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  198. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_307_task4_ask_reply.py +0 -0
  199. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  200. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_319_refusal_exit_codes.py +0 -0
  201. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_323_mistral_backend.py +0 -0
  202. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_332_report_organize_findings.py +0 -0
  203. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_356_query_filters.py +0 -0
  204. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_397_gh_identity_router.py +0 -0
  205. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_397_router_preflight.py +0 -0
  206. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_402_one_identity_resolver.py +0 -0
  207. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  208. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_472_register_caller_binding.py +0 -0
  209. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_523_guide.py +0 -0
  210. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_524_conftest_hermeticity.py +0 -0
  211. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_532_accept_flag.py +0 -0
  212. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_535_version_capability.py +0 -0
  213. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_546_default_gateway.py +0 -0
  214. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_547_verbs_from_registry.py +0 -0
  215. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_548_gateway_remainder.py +0 -0
  216. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_552_codegraph_gateway.py +0 -0
  217. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_555_relay_row_shape.py +0 -0
  218. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_556_relay_freshness.py +0 -0
  219. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_559_edges_truncated.py +0 -0
  220. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  221. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  222. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_579_register_bootstrap_message.py +0 -0
  223. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_591_cli_step_graph.py +0 -0
  224. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_648_install_unit.py +0 -0
  225. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_654_reexec.py +0 -0
  226. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_656_owned_slm.py +0 -0
  227. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_684_dreaming_verb.py +0 -0
  228. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_689_pkg_version_order.py +0 -0
  229. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_715_grok_wake.py +0 -0
  230. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_716_timeline_one_path.py +0 -0
  231. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_723_grok_0251.py +0 -0
  232. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  233. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_725_stdio_reconfigure.py +0 -0
  234. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_726_grok_limit_screen.py +0 -0
  235. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_729_dm_once.py +0 -0
  236. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  237. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_735_dm_mcp_tools.py +0 -0
  238. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_740_edit_project.py +0 -0
  239. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_762_cards_edit_priority.py +0 -0
  240. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_764_enrich_collapse.py +0 -0
  241. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_801_confirm_flow_cli.py +0 -0
  242. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_802_stage_history.py +0 -0
  243. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_825_codegraph_hook_noise.py +0 -0
  244. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_825_install_posttooluse_only.py +0 -0
  245. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  246. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_829_counterfactual_column.py +0 -0
  247. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_830_hook_binding_version.py +0 -0
  248. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_84_cli_labels.py +0 -0
  249. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_866_doctrine_guide.py +0 -0
  250. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_872_identity_refuses_not_warns.py +0 -0
  251. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_874_source_text.py +0 -0
  252. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  253. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_937_dreaming_notify.py +0 -0
  254. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_938_organize_line_limit.py +0 -0
  255. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_941_bench_backends.py +0 -0
  256. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_941_harness_342.py +0 -0
  257. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_945_same_line_pairing.py +0 -0
  258. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_955_prompt_audit.py +0 -0
  259. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_957_auth_http.py +0 -0
  260. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_959_venv_interpreter.py +0 -0
  261. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_960_channel.py +0 -0
  262. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  263. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_963_plugin_manifest.py +0 -0
  264. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_964_channel_cell.py +0 -0
  265. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_965_mesh_wait.py +0 -0
  266. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_975_channel_hop.py +0 -0
  267. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_add_security_gate.py +0 -0
  268. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_add.py +0 -0
  269. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_hash.py +0 -0
  270. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_lib.py +0 -0
  271. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_mcp.py +0 -0
  272. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_skill.py +0 -0
  273. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_tool.py +0 -0
  274. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_artifact_uri.py +0 -0
  275. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_backends.py +0 -0
  276. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_claude_cli.py +0 -0
  277. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_command.py +0 -0
  278. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_pack.py +0 -0
  279. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_prices.py +0 -0
  280. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_quality.py +0 -0
  281. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_runner.py +0 -0
  282. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_bench_validate.py +0 -0
  283. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_board_card_thread.py +0 -0
  284. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_board_command.py +0 -0
  285. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_board_merge_check.py +0 -0
  286. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_board_obligation_close.py +0 -0
  287. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_brain_ask_command.py +0 -0
  288. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  289. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  290. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_brain_command.py +0 -0
  291. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_caller_meta_guard.py +0 -0
  292. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_capture_lineage.py +0 -0
  293. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_capture_liveness.py +0 -0
  294. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_capture_manifest.py +0 -0
  295. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_capture_paths.py +0 -0
  296. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_capture_security.py +0 -0
  297. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cell_command_dispatch.py +0 -0
  298. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cell_harden.py +0 -0
  299. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cell_loader.py +0 -0
  300. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cell_selfcheck.py +0 -0
  301. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cell_verify.py +0 -0
  302. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_chain_token.py +0 -0
  303. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_channel_command.py +0 -0
  304. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  305. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_channel_post.py +0 -0
  306. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_channel_wake_policy_filter.py +0 -0
  307. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_chat_command.py +0 -0
  308. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_claude_parser.py +0 -0
  309. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_claude_tmux_template.py +0 -0
  310. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_codegraph_caller_scope.py +0 -0
  311. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_codegraph_command.py +0 -0
  312. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_codegraph_hook.py +0 -0
  313. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_codegraph_mcp.py +0 -0
  314. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_codex_waker.py +0 -0
  315. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_compress_caller_convention.py +0 -0
  316. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_compress_command.py +0 -0
  317. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_compress_levers.py +0 -0
  318. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_compress_marker.py +0 -0
  319. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_compress_verify.py +0 -0
  320. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_console_encoding_e2e.py +0 -0
  321. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_content_composition.py +0 -0
  322. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_cursor_membrane.py +0 -0
  323. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_daemon_command.py +0 -0
  324. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_daemon_console_encoding.py +0 -0
  325. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_daemon_delivery.py +0 -0
  326. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_delivery_queue.py +0 -0
  327. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_display_sanitize.py +0 -0
  328. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  329. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_event_emit.py +0 -0
  330. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_feature_to_uri.py +0 -0
  331. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_gateway_brain.py +0 -0
  332. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_gateway_client.py +0 -0
  333. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_gateway_command.py +0 -0
  334. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_gateway_message_idempotency.py +0 -0
  335. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  336. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_group_command.py +0 -0
  337. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_guide_commands_parse.py +0 -0
  338. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_guide_selfcheck.py +0 -0
  339. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_heartbeat_check.py +0 -0
  340. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_highlight_command.py +0 -0
  341. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hook_output.py +0 -0
  342. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_activity_marker.py +0 -0
  343. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_add.py +0 -0
  344. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_bundle.py +0 -0
  345. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_init.py +0 -0
  346. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  347. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_lifecycle.py +0 -0
  348. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_merge.py +0 -0
  349. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_verify.py +0 -0
  350. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  351. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_hooks_win32_command_path.py +0 -0
  352. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_import_command.py +0 -0
  353. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_inbox_identity_provenance.py +0 -0
  354. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_init_command.py +0 -0
  355. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_init_provider_menu.py +0 -0
  356. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_install_codex_hooks.py +0 -0
  357. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_install_hook.py +0 -0
  358. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_install_multiplexer_command.py +0 -0
  359. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_install_postcompact_hook.py +0 -0
  360. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_install_wake_hook.py +0 -0
  361. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_lane_command.py +0 -0
  362. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_main.py +0 -0
  363. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_main_unknown_verb_guard.py +0 -0
  364. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mcp_missing_hint.py +0 -0
  365. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mcp_server.py +0 -0
  366. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_memory_command.py +0 -0
  367. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_memory_emit_snippet.py +0 -0
  368. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_memory_gateway.py +0 -0
  369. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_memory_sync.py +0 -0
  370. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_memory_sync_membrane.py +0 -0
  371. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_command.py +0 -0
  372. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  373. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  374. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_originate_thread_623.py +0 -0
  375. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  376. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_sidecar.py +0 -0
  377. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  378. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  379. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_meta_edge_identity.py +0 -0
  380. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_monitor_channel_poll.py +0 -0
  381. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_monitor_cli.py +0 -0
  382. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_monitor_daemon_collision.py +0 -0
  383. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_monitor_self_name_verification.py +0 -0
  384. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_monitor_state_split.py +0 -0
  385. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_multiplexer.py +0 -0
  386. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_muse_membrane.py +0 -0
  387. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_muse_own_cli.py +0 -0
  388. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  389. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_okf_links.py +0 -0
  390. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_onboard_command.py +0 -0
  391. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_opencode_membrane.py +0 -0
  392. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_opencode_plugin.py +0 -0
  393. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_orphan_daemons.py +0 -0
  394. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_packaged_artifacts_exist.py +0 -0
  395. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_packaged_skills.py +0 -0
  396. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_executor.py +0 -0
  397. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_executor_windows.py +0 -0
  398. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_command.py +0 -0
  399. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  400. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_reply_outbox.py +0 -0
  401. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_reply_transport.py +0 -0
  402. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_service_e2e.py +0 -0
  403. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_service_host.py +0 -0
  404. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_service_payload.py +0 -0
  405. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_peer_service_reply.py +0 -0
  406. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_postcompact_recall.py +0 -0
  407. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_process_liveness.py +0 -0
  408. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_protocol_handler.py +0 -0
  409. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_providers_isolation.py +0 -0
  410. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_ratify_command.py +0 -0
  411. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  412. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_repl_caller_convention.py +0 -0
  413. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_schedule_command.py +0 -0
  414. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_schedule_create_contract.py +0 -0
  415. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_security.py +0 -0
  416. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_service_app.py +0 -0
  417. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_service_command.py +0 -0
  418. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_service_providers.py +0 -0
  419. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_session_bridge_inject.py +0 -0
  420. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_session_bridge_probe.py +0 -0
  421. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_session_bridge_resolve.py +0 -0
  422. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_session_path_role_gate.py +0 -0
  423. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  424. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_smoke_chat.py +0 -0
  425. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_smoke_one_shot.py +0 -0
  426. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_smoke_phase_5_5.py +0 -0
  427. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_base_hoist.py +0 -0
  428. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_command.py +0 -0
  429. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_identity_stamp.py +0 -0
  430. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_live_pin.py +0 -0
  431. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  432. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_tmux_session.py +0 -0
  433. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_spawn_windows_relaunch.py +0 -0
  434. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_stall_alert.py +0 -0
  435. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_subprocess_decode_encoding.py +0 -0
  436. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_timeline_command.py +0 -0
  437. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  438. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  439. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_tmux_wake_stale.py +0 -0
  440. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  441. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_token_file_one_parser.py +0 -0
  442. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_token_precedence_explicit_first.py +0 -0
  443. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_unified_token_resolver.py +0 -0
  444. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_version_command.py +0 -0
  445. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_version_consistency.py +0 -0
  446. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_vibe_membrane.py +0 -0
  447. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog.py +0 -0
  448. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake.py +0 -0
  449. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  450. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  451. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_emit_health.py +0 -0
  452. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_install_flags.py +0 -0
  453. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_model_rung.py +0 -0
  454. {swarph_cli-0.69.8 → swarph_cli-0.70.0}/tests/test_watchdog_stale_peers.py +0 -0
  455. {swarph_cli-0.69.8 → 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.8
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.8"
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.8"
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)
@@ -326,6 +326,38 @@ def _read_cgroup(pid: int) -> "str | None":
326
326
  return None
327
327
 
328
328
 
329
+ def _service_leaf(text: "str | None") -> "str | None":
330
+ """The .service leaf of a cgroup, or None.
331
+
332
+ user@<uid>.service is the session manager, not a supervisor. The leaf
333
+ keeps its .service suffix so a later reader can name the unit after
334
+ this process is gone.
335
+ """
336
+ if not text:
337
+ return None
338
+ for line in text.splitlines():
339
+ leaf = line.rsplit("/", 1)[-1].strip()
340
+ if leaf.endswith(".service") and not leaf.startswith("user@"):
341
+ return leaf
342
+ return None
343
+
344
+
345
+ def _supervisor_for_pidfile(explicit: "str | None") -> "str | None":
346
+ """What start writes into monitor.pid.
347
+
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.
352
+ """
353
+ if explicit:
354
+ return explicit
355
+ leaf = _service_leaf(_read_cgroup(os.getpid()))
356
+ if leaf and leaf.startswith("swarph-monitor"):
357
+ return leaf
358
+ return None
359
+
360
+
329
361
  def _derive_systemd_unit(pid: "int | None") -> "str | None":
330
362
  """The supervisor Linux already knows (#344 review): Windows has no
331
363
  pid→task map so the pidfile CLAIM is the whole answer there, but on
@@ -589,10 +621,11 @@ def _cmd_start(args: argparse.Namespace) -> int:
589
621
  )
590
622
  return 0
591
623
 
624
+ recorded = _supervisor_for_pidfile(supervisor)
592
625
  if args.once:
593
626
  mesh.write_pidfile(
594
627
  pidfile, self_name=self_name, sinks=sinks, poll_s=args.poll_s,
595
- supervisor=supervisor,
628
+ supervisor=recorded,
596
629
  )
597
630
  mesh._monitor_iteration(state)
598
631
  return 0
@@ -602,22 +635,26 @@ def _cmd_start(args: argparse.Namespace) -> int:
602
635
  print("swarph monitor: no fork() on this platform — running in the "
603
636
  "foreground", file=sys.stderr)
604
637
  return _run_pinned(state, pidfile, self_name, sinks, args.poll_s,
605
- supervisor=supervisor)
638
+ supervisor=recorded)
606
639
 
607
640
  return _start_detached(state, pidfile, self_name, sinks, args.poll_s,
608
- supervisor=supervisor)
641
+ supervisor=recorded)
609
642
 
610
643
 
611
644
  def _run_pinned(state, pidfile: Path, self_name: str, sinks: list, poll_s: int,
612
645
  supervisor: "str | None" = None) -> int:
613
646
  mesh.write_pidfile(pidfile, self_name=self_name, sinks=sinks, poll_s=poll_s,
614
647
  supervisor=supervisor)
648
+ clean = False
615
649
  try:
616
- return mesh._monitor_loop(state)
650
+ result = mesh._monitor_loop(state)
651
+ clean = True
652
+ return result
617
653
  finally:
618
- # Only clear the pidfile if it is still OURS — a racing `start` may have
619
- # legitimately reclaimed it, and deleting its record would strand it.
620
- 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":
621
658
  pidfile.unlink(missing_ok=True)
622
659
 
623
660