swarph-cli 0.68.0__tar.gz → 0.68.2__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 (442) hide show
  1. {swarph_cli-0.68.0/src/swarph_cli.egg-info → swarph_cli-0.68.2}/PKG-INFO +1 -1
  2. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/pyproject.toml +1 -1
  3. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hooks.py +61 -17
  5. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/mesh.py +77 -7
  6. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/schedule.py +1 -1
  7. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/watchdog.py +106 -31
  8. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/server.py +1 -1
  9. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/wake_watchdog.py +18 -10
  10. {swarph_cli-0.68.0 → swarph_cli-0.68.2/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  11. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
  12. swarph_cli-0.68.2/tests/test_184_notify_honesty.py +114 -0
  13. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_729_wake_watchdog.py +20 -0
  14. swarph_cli-0.68.2/tests/test_825_install_posttooluse_only.py +105 -0
  15. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_825_remaining_cap_shred_bind.py +4 -4
  16. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_830_hook_binding_version.py +2 -2
  17. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_hook.py +1 -3
  18. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar.py +12 -1
  19. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake.py +3 -2
  20. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake_cooldown.py +7 -6
  21. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake_wiring.py +4 -2
  22. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_model_rung.py +4 -1
  23. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/LICENSE +0 -0
  24. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/README.md +0 -0
  25. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/setup.cfg +0 -0
  26. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/__main__.py +0 -0
  27. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/__init__.py +0 -0
  28. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/backends.py +0 -0
  29. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/pack.py +0 -0
  30. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/prices.py +0 -0
  31. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/providers.py +0 -0
  32. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/quality.py +0 -0
  33. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/refresh_prices.py +0 -0
  34. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/rules.py +0 -0
  35. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/runner.py +0 -0
  36. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/validate.py +0 -0
  37. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/caller.py +0 -0
  38. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/__init__.py +0 -0
  39. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/harden.py +0 -0
  40. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/lineage.py +0 -0
  41. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/liveness.py +0 -0
  42. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/manifest.py +0 -0
  43. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/paths.py +0 -0
  44. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/verify.py +0 -0
  45. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/cell.py +0 -0
  46. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/chain_token.py +0 -0
  47. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/channel.py +0 -0
  48. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/__init__.py +0 -0
  49. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_content.py +0 -0
  50. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_display.py +0 -0
  51. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_gateway_client.py +0 -0
  52. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/add.py +0 -0
  53. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/bench.py +0 -0
  54. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/board.py +0 -0
  55. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/board_merge_check.py +0 -0
  56. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/brain.py +0 -0
  57. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/brain_ask.py +0 -0
  58. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell.py +0 -0
  59. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell_probe.py +0 -0
  60. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  61. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/channel.py +0 -0
  62. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/chat.py +0 -0
  63. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codegraph.py +0 -0
  64. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  65. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  66. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codex_waker.py +0 -0
  67. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/compress.py +0 -0
  68. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/daemon.py +0 -0
  69. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/dreaming.py +0 -0
  70. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/event.py +0 -0
  71. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/gateway.py +0 -0
  72. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/gh_route.py +0 -0
  73. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/group.py +0 -0
  74. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/guide.py +0 -0
  75. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/highlight.py +0 -0
  76. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  77. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hook_output.py +0 -0
  78. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/import_session.py +0 -0
  79. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/init.py +0 -0
  80. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  81. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_hook.py +0 -0
  82. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  83. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  84. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  85. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  86. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/lane.py +0 -0
  87. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/mcp_server.py +0 -0
  88. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory.py +0 -0
  89. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  90. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory_sync.py +0 -0
  91. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/monitor.py +0 -0
  92. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/okf_links.py +0 -0
  93. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/onboard.py +0 -0
  94. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  95. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  96. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/protocol_handler.py +0 -0
  97. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/ratify.py +0 -0
  98. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/security.py +0 -0
  99. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/service.py +0 -0
  100. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/spawn.py +0 -0
  101. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/timeline.py +0 -0
  102. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/version.py +0 -0
  103. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  104. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/__init__.py +0 -0
  105. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/levers.py +0 -0
  106. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/marker.py +0 -0
  107. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/verify.py +0 -0
  108. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/console_safe.py +0 -0
  109. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/delivery_queue.py +0 -0
  110. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dm_frame.py +0 -0
  111. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/__init__.py +0 -0
  112. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/candidates.py +0 -0
  113. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/clone.py +0 -0
  114. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/enrich.py +0 -0
  115. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/index_check.py +0 -0
  116. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/notify.py +0 -0
  117. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/organize.py +0 -0
  118. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/probes.py +0 -0
  119. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/report.py +0 -0
  120. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/run.py +0 -0
  121. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/slm.py +0 -0
  122. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/transcripts.py +0 -0
  123. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/verify.py +0 -0
  124. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/__init__.py +0 -0
  125. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/feature_registry.py +0 -0
  126. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/lanes_control.py +0 -0
  127. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/schema.sql +0 -0
  128. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/services_control.py +0 -0
  129. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway_default.py +0 -0
  130. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gh_identity.py +0 -0
  131. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/guide/GUIDE.md +0 -0
  132. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/main.py +0 -0
  133. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/multiplexer.py +0 -0
  134. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/orphan_daemons.py +0 -0
  135. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/pane_probe.py +0 -0
  136. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/parsers/__init__.py +0 -0
  137. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/parsers/claude.py +0 -0
  138. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  139. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  140. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  141. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  142. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  143. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  144. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  145. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  146. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  147. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  148. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  149. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  150. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_executor.py +0 -0
  151. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_executor_windows.py +0 -0
  152. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_reconciliation.py +0 -0
  153. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_reply_outbox.py +0 -0
  154. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_host.py +0 -0
  155. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_payload.py +0 -0
  156. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_reply.py +0 -0
  157. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_staging.py +0 -0
  158. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/query_filters.py +0 -0
  159. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/__init__.py +0 -0
  160. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  161. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  162. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  163. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  164. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/__init__.py +0 -0
  165. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/app.py +0 -0
  166. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/providers.py +0 -0
  167. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/session_bridge.py +0 -0
  168. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  169. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  170. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/source_text.py +0 -0
  171. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/stall_alert.py +0 -0
  172. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/codex-waker.default +0 -0
  173. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  174. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  175. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  176. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  177. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  178. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  179. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  180. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  181. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  182. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  183. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  184. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  185. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  186. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/timeline_paths.py +0 -0
  187. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/tokens.py +0 -0
  188. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  189. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  190. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/requires.txt +0 -0
  191. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/top_level.txt +0 -0
  192. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_124_register_merge.py +0 -0
  193. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_126_pending_from_complete.py +0 -0
  194. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  195. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_138_memory_navigate.py +0 -0
  196. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_139_tool_probe.py +0 -0
  197. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_183_card_anchor.py +0 -0
  198. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_201_win32_bash_refusal.py +0 -0
  199. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_243_onboard_per_peer_token.py +0 -0
  200. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_307_task4_ask_reply.py +0 -0
  201. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  202. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_319_refusal_exit_codes.py +0 -0
  203. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_323_mistral_backend.py +0 -0
  204. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_332_report_organize_findings.py +0 -0
  205. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_356_query_filters.py +0 -0
  206. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_397_gh_identity_router.py +0 -0
  207. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_397_router_preflight.py +0 -0
  208. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_467b_onboard_operator_credential.py +0 -0
  209. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_523_guide.py +0 -0
  210. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_524_conftest_hermeticity.py +0 -0
  211. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_532_accept_flag.py +0 -0
  212. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_535_version_capability.py +0 -0
  213. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_546_default_gateway.py +0 -0
  214. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_547_verbs_from_registry.py +0 -0
  215. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_548_gateway_remainder.py +0 -0
  216. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_552_codegraph_gateway.py +0 -0
  217. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_555_relay_row_shape.py +0 -0
  218. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_556_relay_freshness.py +0 -0
  219. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_559_edges_truncated.py +0 -0
  220. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_564_bootstrap_first_peer.py +0 -0
  221. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  222. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_579_register_bootstrap_message.py +0 -0
  223. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_591_cli_step_graph.py +0 -0
  224. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_644_monitor_supervision.py +0 -0
  225. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_648_install_unit.py +0 -0
  226. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_654_reexec.py +0 -0
  227. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_656_owned_slm.py +0 -0
  228. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_684_dreaming_verb.py +0 -0
  229. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_689_pkg_version_order.py +0 -0
  230. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_716_timeline_one_path.py +0 -0
  231. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  232. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_725_stdio_reconfigure.py +0 -0
  233. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_729_dm_once.py +0 -0
  234. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  235. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_735_dm_mcp_tools.py +0 -0
  236. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_740_edit_project.py +0 -0
  237. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_762_cards_edit_priority.py +0 -0
  238. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_764_enrich_collapse.py +0 -0
  239. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_801_confirm_flow_cli.py +0 -0
  240. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_802_stage_history.py +0 -0
  241. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_825_codegraph_hook_noise.py +0 -0
  242. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_829_counterfactual_column.py +0 -0
  243. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_84_cli_labels.py +0 -0
  244. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_866_doctrine_guide.py +0 -0
  245. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_872_identity_refuses_not_warns.py +0 -0
  246. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_874_source_text.py +0 -0
  247. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  248. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_937_dreaming_notify.py +0 -0
  249. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_938_organize_line_limit.py +0 -0
  250. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_941_bench_backends.py +0 -0
  251. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_941_harness_342.py +0 -0
  252. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_945_same_line_pairing.py +0 -0
  253. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_955_prompt_audit.py +0 -0
  254. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_957_auth_http.py +0 -0
  255. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_959_venv_interpreter.py +0 -0
  256. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_960_channel.py +0 -0
  257. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_963_plugin_manifest.py +0 -0
  258. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_964_channel_cell.py +0 -0
  259. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_965_mesh_wait.py +0 -0
  260. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_975_channel_hop.py +0 -0
  261. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_add_security_gate.py +0 -0
  262. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_add.py +0 -0
  263. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_hash.py +0 -0
  264. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_lib.py +0 -0
  265. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_mcp.py +0 -0
  266. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_skill.py +0 -0
  267. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_tool.py +0 -0
  268. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_uri.py +0 -0
  269. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_backends.py +0 -0
  270. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_command.py +0 -0
  271. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_pack.py +0 -0
  272. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_prices.py +0 -0
  273. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_quality.py +0 -0
  274. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_runner.py +0 -0
  275. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_validate.py +0 -0
  276. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_card_thread.py +0 -0
  277. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_command.py +0 -0
  278. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_merge_check.py +0 -0
  279. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_obligation_close.py +0 -0
  280. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_command.py +0 -0
  281. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_env_diagnosis.py +0 -0
  282. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_identity_explicitness.py +0 -0
  283. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_command.py +0 -0
  284. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_caller_meta_guard.py +0 -0
  285. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_lineage.py +0 -0
  286. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_liveness.py +0 -0
  287. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_manifest.py +0 -0
  288. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_paths.py +0 -0
  289. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_security.py +0 -0
  290. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_command_dispatch.py +0 -0
  291. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_harden.py +0 -0
  292. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_loader.py +0 -0
  293. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_selfcheck.py +0 -0
  294. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_verify.py +0 -0
  295. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_chain_token.py +0 -0
  296. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_command.py +0 -0
  297. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  298. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_post.py +0 -0
  299. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_wake_policy_filter.py +0 -0
  300. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_chat_command.py +0 -0
  301. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_claude_parser.py +0 -0
  302. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_claude_tmux_template.py +0 -0
  303. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_caller_scope.py +0 -0
  304. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_command.py +0 -0
  305. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_mcp.py +0 -0
  306. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codex_waker.py +0 -0
  307. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_caller_convention.py +0 -0
  308. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_command.py +0 -0
  309. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_levers.py +0 -0
  310. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_marker.py +0 -0
  311. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_verify.py +0 -0
  312. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_console_encoding_e2e.py +0 -0
  313. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_content_composition.py +0 -0
  314. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cursor_membrane.py +0 -0
  315. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_command.py +0 -0
  316. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_console_encoding.py +0 -0
  317. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_delivery.py +0 -0
  318. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_delivery_queue.py +0 -0
  319. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_display_sanitize.py +0 -0
  320. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  321. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_event_emit.py +0 -0
  322. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_feature_to_uri.py +0 -0
  323. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_brain.py +0 -0
  324. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_client.py +0 -0
  325. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_command.py +0 -0
  326. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_message_idempotency.py +0 -0
  327. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  328. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_group_command.py +0 -0
  329. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_guide_commands_parse.py +0 -0
  330. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_guide_selfcheck.py +0 -0
  331. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_heartbeat_check.py +0 -0
  332. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_highlight_command.py +0 -0
  333. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hook_output.py +0 -0
  334. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_activity_marker.py +0 -0
  335. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_add.py +0 -0
  336. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_bundle.py +0 -0
  337. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_init.py +0 -0
  338. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_legacy_generation_removal.py +0 -0
  339. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_lifecycle.py +0 -0
  340. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_merge.py +0 -0
  341. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_verify.py +0 -0
  342. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  343. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_win32_command_path.py +0 -0
  344. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_import_command.py +0 -0
  345. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_inbox_identity_provenance.py +0 -0
  346. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_init_command.py +0 -0
  347. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_init_provider_menu.py +0 -0
  348. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_codex_hooks.py +0 -0
  349. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_hook.py +0 -0
  350. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_multiplexer_command.py +0 -0
  351. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_postcompact_hook.py +0 -0
  352. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_wake_hook.py +0 -0
  353. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_lane_command.py +0 -0
  354. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_main.py +0 -0
  355. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_main_unknown_verb_guard.py +0 -0
  356. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mcp_missing_hint.py +0 -0
  357. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mcp_server.py +0 -0
  358. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_command.py +0 -0
  359. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_emit_snippet.py +0 -0
  360. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_gateway.py +0 -0
  361. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_sync.py +0 -0
  362. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_sync_membrane.py +0 -0
  363. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_command.py +0 -0
  364. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_inbox_mark_read.py +0 -0
  365. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_inbox_sanitize.py +0 -0
  366. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_originate_thread_623.py +0 -0
  367. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_send_unknown_peer.py +0 -0
  368. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  369. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  370. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_meta_edge_identity.py +0 -0
  371. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_channel_poll.py +0 -0
  372. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_cli.py +0 -0
  373. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_daemon_collision.py +0 -0
  374. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_self_name_verification.py +0 -0
  375. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_state_split.py +0 -0
  376. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_multiplexer.py +0 -0
  377. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_muse_membrane.py +0 -0
  378. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_muse_own_cli.py +0 -0
  379. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_obligation_close_is_surfaced.py +0 -0
  380. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_okf_links.py +0 -0
  381. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_onboard_command.py +0 -0
  382. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_opencode_membrane.py +0 -0
  383. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_opencode_plugin.py +0 -0
  384. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_orphan_daemons.py +0 -0
  385. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_packaged_artifacts_exist.py +0 -0
  386. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_packaged_skills.py +0 -0
  387. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_executor.py +0 -0
  388. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_executor_windows.py +0 -0
  389. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_drain_command.py +0 -0
  390. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_drain_supervisor.py +0 -0
  391. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_outbox.py +0 -0
  392. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_transport.py +0 -0
  393. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_e2e.py +0 -0
  394. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_host.py +0 -0
  395. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_payload.py +0 -0
  396. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_reply.py +0 -0
  397. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_postcompact_recall.py +0 -0
  398. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_process_liveness.py +0 -0
  399. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_protocol_handler.py +0 -0
  400. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_providers_isolation.py +0 -0
  401. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_ratify_command.py +0 -0
  402. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_readme_claims_no_gateway_default.py +0 -0
  403. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_repl_caller_convention.py +0 -0
  404. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_schedule_command.py +0 -0
  405. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_schedule_create_contract.py +0 -0
  406. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_security.py +0 -0
  407. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_app.py +0 -0
  408. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_command.py +0 -0
  409. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_providers.py +0 -0
  410. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_inject.py +0 -0
  411. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_probe.py +0 -0
  412. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_resolve.py +0 -0
  413. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_path_role_gate.py +0 -0
  414. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_shipped_units_match_the_cli.py +0 -0
  415. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_chat.py +0 -0
  416. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_one_shot.py +0 -0
  417. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_phase_5_5.py +0 -0
  418. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_base_hoist.py +0 -0
  419. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_command.py +0 -0
  420. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_identity_stamp.py +0 -0
  421. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_live_pin.py +0 -0
  422. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_mitosis_lineage.py +0 -0
  423. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_tmux_session.py +0 -0
  424. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_windows_relaunch.py +0 -0
  425. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_stall_alert.py +0 -0
  426. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_subprocess_decode_encoding.py +0 -0
  427. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_timeline_command.py +0 -0
  428. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_runstate_defer.py +0 -0
  429. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_session_reanchor.py +0 -0
  430. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_stale.py +0 -0
  431. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_submit_verify.py +0 -0
  432. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_token_file_one_parser.py +0 -0
  433. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_token_precedence_explicit_first.py +0 -0
  434. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_unified_token_resolver.py +0 -0
  435. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_version_command.py +0 -0
  436. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_version_consistency.py +0 -0
  437. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_vibe_membrane.py +0 -0
  438. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog.py +0 -0
  439. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_emit_health.py +0 -0
  440. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_install_flags.py +0 -0
  441. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_stale_peers.py +0 -0
  442. {swarph_cli-0.68.0 → swarph_cli-0.68.2}/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.68.0
3
+ Version: 0.68.2
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.68.0"
7
+ version = "0.68.2"
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.68.0"
19
+ __version__ = "0.68.2"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -190,17 +190,17 @@ exit 0
190
190
  # The bundled codegraph hook (cards #194 + #825). Thin wrapper around
191
191
  # `swarph codegraph-hook` so heuristics stay unit-testable.
192
192
  #
193
- # #825: UserPromptSubmit is the load-bearing binding — fires while tool choice
194
- # is still open. PostToolUse/Bash is kept initially for a shared audit series
195
- # (drop later if audit shows zero influence). Stop/StopFailure close the
196
- # counterfactual (subsequent=neither).
193
+ # #825, retired 2026-09-26: UserPromptSubmit / Stop / StopFailure are not
194
+ # installed. The audit showed the prompt side did not steer tool choice
195
+ # (3 codegraph calls in 1,206 displays). PostToolUse/Bash is the binding
196
+ # that stays. install_hook drops the retired events for this command so a
197
+ # later `swarph hooks add` cannot put them back.
197
198
  _CODEGRAPH_ON_GREP_SH = r"""#!/bin/sh
198
199
  # codegraph-on-grep.sh — swarph bundled Claude Code hook (#194 + #825).
199
200
  # swarph-cli-bundle-version: __SWARPH_CLI_VERSION__
200
201
  #
201
- # UserPromptSubmit: coding keywords in the prompt → structural codegraph context
202
- # before a tool is chosen. PostToolUse/Bash: annotate code greps (shared audit).
203
- # Stop/StopFailure: record whether the session grepped, called codegraph, or neither.
202
+ # PostToolUse/Bash: annotate code greps. UserPromptSubmit, Stop, and
203
+ # StopFailure are not bound (card #825, retired 2026-09-26).
204
204
  # #830: version stamp is substituted at install time — hooks list compares it to
205
205
  # the running swarph --version so a binding/code skew is reportable.
206
206
  exec swarph codegraph-hook "$@"
@@ -210,10 +210,8 @@ BUILTIN_HOOKS: dict = {
210
210
  "codegraph-on-grep": HookBundle(
211
211
  name="codegraph-on-grep",
212
212
  description=(
213
- "On UserPromptSubmit (coding keywords) and PostToolUse/Bash (code "
214
- "grep), queries the gateway's structural codegraph and injects "
215
- "symbol-level answers. Prompt path steers tool choice; Bash path "
216
- "annotates. Audits counterfactual outcomes. Never blocks. Reports "
213
+ "On PostToolUse/Bash (code grep), queries the gateway's structural "
214
+ "codegraph and injects symbol-level answers. Never blocks. Reports "
217
215
  "an unavailable index LOUDLY rather than as 'no matches'."
218
216
  ),
219
217
  publisher="swarph-builtin",
@@ -221,13 +219,10 @@ BUILTIN_HOOKS: dict = {
221
219
  script_name="codegraph-on-grep.sh",
222
220
  script_body=_CODEGRAPH_ON_GREP_SH,
223
221
  bindings=(
224
- HookBinding("UserPromptSubmit", ""),
225
222
  HookBinding("PostToolUse", "Bash"),
226
- HookBinding("Stop", ""),
227
- HookBinding("StopFailure", ""),
228
223
  ),
229
224
  handler_events=(
230
- "UserPromptSubmit", "PostToolUse", "Stop", "StopFailure",
225
+ "PostToolUse",
231
226
  ),
232
227
  ),
233
228
  "cell-resilience": HookBundle(
@@ -502,6 +497,49 @@ def _unmerge_hook(settings: dict, event: str, matcher: str, command: str) -> dic
502
497
  return settings
503
498
 
504
499
 
500
+ def _drop_command_outside_bindings(settings: dict, commands, bundle: HookBundle) -> dict:
501
+ """Remove this bundle's command strings from events it does not declare.
502
+
503
+ ``_merge_hook`` only adds. A binding deleted from the bundle would
504
+ otherwise survive the next install, which is how the retired codegraph
505
+ prompt-side events came back (card #825). ``commands`` is the install
506
+ ladder (canonical plus older Windows forms). Other commands on those
507
+ events are left alone.
508
+ """
509
+ keep = {(b.event, b.matcher) for b in bundle.bindings}
510
+ hooks = settings.get("hooks")
511
+ if not isinstance(hooks, dict):
512
+ return settings
513
+ drop = {v for c in commands for v in _command_variants_for(c)}
514
+ for event, entries in list(hooks.items()):
515
+ if not isinstance(entries, list):
516
+ continue
517
+ removed = False
518
+ for entry in list(entries):
519
+ # A string (or anything else) in an unrelated event is not a
520
+ # group. Skip it. _unmerge_hook would call .get on it (#825).
521
+ if not isinstance(entry, dict):
522
+ continue
523
+ acts = entry.get("hooks")
524
+ if not isinstance(acts, list) or (event, entry.get("matcher", "")) in keep:
525
+ continue
526
+ kept = [
527
+ a for a in acts
528
+ if not (isinstance(a, dict) and a.get("command") in drop)
529
+ ]
530
+ # Never rewrite a group that did not hold this command. An empty
531
+ # hooks list and a matcher-only group stay as they were.
532
+ if len(kept) == len(acts):
533
+ continue
534
+ entry["hooks"] = kept
535
+ if not kept:
536
+ entries.remove(entry)
537
+ removed = True
538
+ if removed and not entries:
539
+ del hooks[event]
540
+ return settings
541
+
542
+
505
543
  # --------------------------------------------------------------------------- #
506
544
  # Local-bundle resolver (T3)
507
545
  # --------------------------------------------------------------------------- #
@@ -699,6 +737,11 @@ def install_hook(
699
737
  settings = _load_settings(settings_path)
700
738
  for b in bundle.bindings:
701
739
  settings = _merge_hook(settings, b.event, b.matcher, command)
740
+ # Drop this command from every event the bundle no longer declares.
741
+ # Otherwise a retired binding (codegraph UserPromptSubmit/Stop, #825)
742
+ # stays forever, and the next add puts it back if it was removed by hand.
743
+ settings = _drop_command_outside_bindings(
744
+ settings, _installed_command_variants(bundle, hooks_home), bundle)
702
745
 
703
746
  # ---- write the script (version-stamped), then atomic-save settings ----
704
747
  hooks_home_p.mkdir(parents=True, exist_ok=True)
@@ -1058,8 +1101,9 @@ def list_hooks(
1058
1101
 
1059
1102
  name [installed|partial|available] trust=builtin — description
1060
1103
 
1061
- ``partial`` (#825): at least one binding present but not the full set —
1062
- the lab-ovh codegraph case (PostToolUse only, UserPromptSubmit/Stop absent).
1104
+ ``partial`` (#825): at least one binding present but not the full set.
1105
+ codegraph-on-grep has a single PostToolUse/Bash binding, so that shape
1106
+ is installed, not partial.
1063
1107
  When installed and the script carries a ``swarph-cli-bundle-version`` stamp
1064
1108
  (#830), a second line reports match / mismatch vs the running package.
1065
1109
  """
@@ -1253,6 +1253,38 @@ def _write_cursor_atomic(path: Path, cursor: dict) -> None:
1253
1253
  os.replace(tmp, path)
1254
1254
 
1255
1255
 
1256
+ def _persist_wake_stamp(state: "MonitorState") -> None:
1257
+ """Rewrite cursor.json once a wake stamp exists.
1258
+
1259
+ The observation save runs before any sink, so it records last_wake_at
1260
+ while the name is still unknown. Without this second write the file
1261
+ keeps 0.0 after a keystroke (#405).
1262
+ """
1263
+ stamps: list[float] = []
1264
+ for led in state.ledgers.values():
1265
+ raw = led.get("last_wake_at")
1266
+ if raw is None:
1267
+ continue
1268
+ try:
1269
+ stamp = float(raw)
1270
+ except (TypeError, ValueError):
1271
+ continue
1272
+ if stamp > 0:
1273
+ stamps.append(stamp)
1274
+ if not stamps:
1275
+ return
1276
+ newest = max(stamps)
1277
+ previous = state.observed.get("last_wake_at")
1278
+ try:
1279
+ previous_f = float(previous) if previous is not None else 0.0
1280
+ except (TypeError, ValueError):
1281
+ previous_f = 0.0
1282
+ if previous_f == newest:
1283
+ return
1284
+ state.observed["last_wake_at"] = newest
1285
+ _write_cursor_atomic(state.cursor_path, dict(state.cursor))
1286
+
1287
+
1256
1288
  # ─────────────────────────────────────────────────────────────────────────────
1257
1289
  # card #122 — pluggable delivery
1258
1290
  #
@@ -1390,6 +1422,18 @@ class NoneSink(Sink):
1390
1422
  super().__init__("none")
1391
1423
 
1392
1424
 
1425
+ def _stamp_wake(led: dict) -> None:
1426
+ """The keystroke, not the delivery report.
1427
+
1428
+ `last_delivery_at` also moves when a standing wake returns True with no
1429
+ keystroke. `last_wake_at` moves only here, beside `last_wake_injected_at`.
1430
+ The name is absent until this runs; 0.0 is not a time.
1431
+ """
1432
+ now = time.time()
1433
+ led["last_wake_at"] = now
1434
+ led["last_wake_injected_at"] = now
1435
+
1436
+
1393
1437
  class TmuxSink(Sink):
1394
1438
  """Poke a tmux pane — the behaviour `mesh sidecar` has always had.
1395
1439
 
@@ -1475,7 +1519,7 @@ class TmuxSink(Sink):
1475
1519
  return None
1476
1520
  ok = _tmux_wake(self.target)
1477
1521
  if ok:
1478
- led["last_wake_injected_at"] = time.time()
1522
+ _stamp_wake(led)
1479
1523
  return True
1480
1524
  if ok is None:
1481
1525
  return None # human adopted mid-settle: defer
@@ -1504,7 +1548,7 @@ class TmuxSink(Sink):
1504
1548
  return None
1505
1549
  ok = _tmux_wake(self.target)
1506
1550
  if ok:
1507
- led["last_wake_injected_at"] = time.time()
1551
+ _stamp_wake(led)
1508
1552
  return True
1509
1553
  if ok is None:
1510
1554
  return None # human adopted mid-settle: defer
@@ -1549,7 +1593,8 @@ class TmuxSink(Sink):
1549
1593
  # The wake text was OBSERVED already sitting in this pane, so
1550
1594
  # the injection anchor stays valid for this session — nudging
1551
1595
  # it does not re-anchor (the text predates us).
1552
- led.setdefault("last_wake_injected_at", time.time())
1596
+ if "last_wake_injected_at" not in led:
1597
+ _stamp_wake(led)
1553
1598
  return True
1554
1599
  return False
1555
1600
  if composer == "busy":
@@ -1576,7 +1621,7 @@ class TmuxSink(Sink):
1576
1621
  return None
1577
1622
  if ok:
1578
1623
  led["wake_outstanding"] = True
1579
- led["last_wake_injected_at"] = time.time()
1624
+ _stamp_wake(led)
1580
1625
  return True
1581
1626
  # False splits by what the pane actually holds: text observably STUCK
1582
1627
  # in the composer -> mark outstanding so the next poll nudges instead
@@ -1898,7 +1943,17 @@ class MonitorState:
1898
1943
  continue
1899
1944
  led = _new_ledger()
1900
1945
  led["last_delivered_id"] = seed
1901
- led["last_delivery_at"] = float(self.observed.get("last_wake_at", 0.0))
1946
+ raw = self.observed.get("last_wake_at")
1947
+ try:
1948
+ stamp = float(raw) if raw is not None else 0.0
1949
+ except (TypeError, ValueError):
1950
+ stamp = 0.0
1951
+ led["last_delivery_at"] = stamp
1952
+ # A positive file value is the only wake time the old cursor had.
1953
+ # 0.0 was the unread default, not a measurement — leave the key
1954
+ # absent so a later read is unknown, not "never".
1955
+ if stamp > 0:
1956
+ led["last_wake_at"] = stamp
1902
1957
  self.ledgers[sink.name] = led
1903
1958
 
1904
1959
  def ledger(self, name: str) -> dict:
@@ -2522,6 +2577,10 @@ def _monitor_iteration(state: MonitorState, *, poll_channels: bool = True) -> No
2522
2577
  # A delivery deferred by the idle guard (or failed against a dead sink) must
2523
2578
  # still land even though no NEW mail arrived, so this runs on every poll.
2524
2579
  _monitor_deliver(state)
2580
+ # The observation cursor is written BEFORE delivery, while last_wake_at is
2581
+ # still unknown. A wake that landed this poll has to be written again or
2582
+ # cursor.json keeps the 0.0 default after a real keystroke (#405).
2583
+ _persist_wake_stamp(state)
2525
2584
 
2526
2585
  # MATERIALIZE new ledgers even if nothing moved them. Found by driving the
2527
2586
  # real CLI: a pure `--deliver pull` monitor never writes ledgers.json until
@@ -2783,7 +2842,14 @@ class _LegacyCursorView(MutableMapping):
2783
2842
  if key == "last_msg_id":
2784
2843
  return self._observed.get("last_msg_id", 0)
2785
2844
  if key == "last_wake_at":
2786
- return float(self._led()["last_delivery_at"])
2845
+ raw = self._led().get("last_wake_at")
2846
+ if raw is None:
2847
+ return None
2848
+ try:
2849
+ stamp = float(raw)
2850
+ except (TypeError, ValueError):
2851
+ return None
2852
+ return stamp if stamp > 0 else None
2787
2853
  if key == "pending_wake":
2788
2854
  return int(self._led()["last_delivered_id"]) < self._observed_id()
2789
2855
  raise KeyError(key)
@@ -2802,7 +2868,11 @@ class _LegacyCursorView(MutableMapping):
2802
2868
  self._led()["last_delivered_id"] = int(value)
2803
2869
  return
2804
2870
  if key == "last_wake_at":
2805
- self._led()["last_delivery_at"] = float(value)
2871
+ stamp = float(value)
2872
+ self._led()["last_wake_at"] = stamp
2873
+ # The idle guard reads last_delivery_at. Legacy callers suppress
2874
+ # it by assigning this key; keep that clock in step.
2875
+ self._led()["last_delivery_at"] = stamp
2806
2876
  return
2807
2877
  if key == "pending_wake":
2808
2878
  self._led()["last_delivered_id"] = 0 if value else self._observed_id()
@@ -256,7 +256,7 @@ def run_schedule(argv: list) -> int:
256
256
  if args.command == "delete":
257
257
  return _run_delete(args)
258
258
  if args.command == "fire-now":
259
- return _run_action(args, "fire-now", "fire-now", "fired")
259
+ return _run_action(args, "fire-now", "fire-now", "marked")
260
260
  parser.error(f"unknown command: {args.command}")
261
261
  except RuntimeError as exc:
262
262
  print(f"swarph schedule: {exc}", file=sys.stderr)
@@ -792,6 +792,72 @@ def _resolve_send_target(name: str, process_name: str = "claude") -> str:
792
792
  return name
793
793
 
794
794
 
795
+ # How long to wait after Enter before reading the composer. tmux rc==0 means
796
+ # the keys were accepted, not that the pane submitted them (#184).
797
+ _SUBMIT_SETTLE_S = 0.6
798
+
799
+
800
+ # Claude's composer is the last ❯ / > line. Everything under the ─── rule is
801
+ # the status footer ("⏵⏵ auto mode on", "✔ Update installed"), which is the
802
+ # last non-empty line of a live pane and is not the composer (#184, #553).
803
+ _COMPOSER_MARKERS = ("❯", ">", "›")
804
+
805
+
806
+ def _composed_text(stdout: str) -> Optional[str]:
807
+ """Text in the composer, with wrapped continuation lines joined.
808
+
809
+ None when no composer marker is present. The footer below the rule is
810
+ not part of the composer.
811
+ """
812
+ lines = (stdout or "").splitlines()
813
+ idx = None
814
+ for i, ln in enumerate(lines):
815
+ if ln.strip().startswith(_COMPOSER_MARKERS):
816
+ idx = i
817
+ if idx is None:
818
+ return None
819
+ first = lines[idx].strip()
820
+ for marker in _COMPOSER_MARKERS:
821
+ if first.startswith(marker):
822
+ first = first[len(marker):].strip()
823
+ break
824
+ chunks = [first] if first else []
825
+ for ln in lines[idx + 1:]:
826
+ s = ln.strip()
827
+ if not s:
828
+ continue
829
+ if "───" in s or s.startswith("─"):
830
+ break
831
+ chunks.append(s)
832
+ return " ".join(chunks)
833
+
834
+
835
+ def _composer_still_holds(target: str, text: str) -> Optional[bool]:
836
+ """Whether the composer, not the status footer, still contains `text`.
837
+
838
+ True = the payload is still composed (accepted, not submitted).
839
+ False = the composer was read and does not hold it.
840
+ None = the pane could not be read, or no composer line was found.
841
+ Callers fail closed on None.
842
+ """
843
+ try:
844
+ result = subprocess.run(
845
+ ["tmux", "capture-pane", "-p", "-t", target],
846
+ capture_output=True, timeout=5, text=True,
847
+ encoding="utf-8", errors="replace",
848
+ )
849
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
850
+ return None
851
+ if result.returncode != 0:
852
+ return None
853
+ composed = _composed_text(result.stdout or "")
854
+ if composed is None:
855
+ return None
856
+ # A wrap inside a token ('la' / 'b-ovh') joins with a space the payload
857
+ # does not have. Fold all whitespace on both sides (#184, #555).
858
+ return re.sub(r"\s+", "", text) in re.sub(r"\s+", "", composed)
859
+
860
+
795
861
  def _tmux_send_keys(
796
862
  name: str, text: str, clear_input: bool = False, process_name: str = "claude"
797
863
  ) -> bool:
@@ -805,6 +871,10 @@ def _tmux_send_keys(
805
871
  slash-command inject (drop seat-A C2): a prose wake merging into typed
806
872
  text is noise, but `/model ...` merging into typed text submits a
807
873
  corrupted command.
874
+
875
+ Returns True only when a follow-up capture shows the payload has left
876
+ the composer line. tmux accepting the keys is not delivery (#184). An
877
+ unreadable pane is False: the wake stays retryable.
808
878
  """
809
879
  target = _resolve_send_target(name, process_name)
810
880
  keys = ["C-u", text, "Enter"] if clear_input else [text, "Enter"]
@@ -813,7 +883,10 @@ def _tmux_send_keys(
813
883
  ["tmux", "send-keys", "-t", target, *keys],
814
884
  capture_output=True, timeout=5,
815
885
  )
816
- return result.returncode == 0
886
+ if result.returncode != 0:
887
+ return False
888
+ time.sleep(_SUBMIT_SETTLE_S)
889
+ return _composer_still_holds(target, text) is False
817
890
  except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
818
891
  return False
819
892
 
@@ -824,12 +897,12 @@ def _dm_wake(
824
897
  target_peer: str,
825
898
  token: str,
826
899
  content: str,
827
- ) -> bool:
828
- """A1-DM: send a cross-host wake DM to a stranded peer on another host.
900
+ ) -> str | bool:
901
+ """A1-DM: POST a wake DM. Never raises.
829
902
 
830
- POSTs a mesh DM (kind=fyi) to ``{gateway}/messages``; the target peer's
831
- sidecar/inbox-watcher then wakes it. Pure send action — never raises (an
832
- alert/wake path must not crash the watchdog); returns True iff 2xx.
903
+ Returns ``"accepted"`` when the gateway stores the row (HTTP 2xx) and
904
+ ``False`` otherwise. ``"accepted"`` is not delivery: this process cannot
905
+ see the recipient ledger, so it must not return True (#184).
833
906
  """
834
907
  try:
835
908
  body = {
@@ -843,7 +916,9 @@ def _dm_wake(
843
916
  body,
844
917
  token,
845
918
  )
846
- return 200 <= status < 300
919
+ if 200 <= status < 300:
920
+ return "accepted"
921
+ return False
847
922
  except Exception:
848
923
  return False
849
924
 
@@ -1113,7 +1188,8 @@ def _notify_peer_event(
1113
1188
  f"(autonomous ladder action; watchdog log on host has the diag)"
1114
1189
  )
1115
1190
  diag["notify_peer"] = peer
1116
- diag["notify_sent"] = _dm_wake(args.gateway, role, peer, token, content)
1191
+ # 2xx stored the row. It did not deliver it (#184).
1192
+ diag["notify_accepted"] = _dm_wake(args.gateway, role, peer, token, content) == "accepted"
1117
1193
 
1118
1194
 
1119
1195
  def _escalate_a2(
@@ -1259,17 +1335,16 @@ def _dm_wake_scan(
1259
1335
  local session check uses.
1260
1336
 
1261
1337
  T4 — per-peer no-spam cooldown. A peer that stays stale across many ticks
1262
- is DM'd ONCE per ``--dm-wake-cooldown-sec`` window, not every tick. The
1338
+ is POSTed ONCE per ``--dm-wake-cooldown-sec`` window, not every tick. The
1263
1339
  cooldown state ``{peer: last_wake_epoch}`` is loaded once at scan start;
1264
- inside the loop a peer is SKIPPED while ``now - last_wake < cooldown_sec``
1265
- (a cooldown-skip does NOT count as a wake). Only a SUCCESSFUL ``_dm_wake``
1266
- stamps the cooldown; the state is saved once after the loop.
1340
+ inside the loop a peer is SKIPPED while ``now - last_wake < cooldown_sec``.
1341
+ Only a gateway-accepted POST stamps the cooldown; a rejected POST is
1342
+ retried next tick. The state is saved once after the loop.
1267
1343
 
1268
1344
  ``now_epoch`` defaults to ``_now()`` (injectable for deterministic tests).
1269
1345
 
1270
- Returns the count of wake DMs that fired successfully THIS tick (0 if none
1271
- / if ``--dm-wake`` is off / if every stale peer was within cooldown). Never
1272
- raises (an alert path must not crash the watchdog).
1346
+ Returns 0. A 2xx is the gateway storing the row, not the recipient's
1347
+ ledger, so this scan does not report a fired wake (#184). Never raises.
1273
1348
  """
1274
1349
  if not args.dm_wake:
1275
1350
  return 0
@@ -1300,20 +1375,21 @@ def _dm_wake_scan(
1300
1375
  # T4 — load the per-peer cooldown map once at scan start.
1301
1376
  state = _load_dm_wake_state(state_path)
1302
1377
 
1303
- fired = 0
1378
+ accepted = 0
1304
1379
  skipped: list[str] = []
1305
1380
  for target in stale:
1306
1381
  last_wake = state.get(target, 0)
1307
1382
  if now_epoch - last_wake < cooldown_sec:
1308
- # Within cooldown — already DM'd this peer recently. Skip; a
1309
- # cooldown-skip must NOT count toward the exit-3 wake tally.
1383
+ # Within cooldown — already POSTed this peer recently. Skip.
1310
1384
  skipped.append(target)
1311
1385
  continue
1312
- if _dm_wake(gateway, self_peer, target, token or "", _DM_WAKE_PROMPT):
1313
- # Only a SUCCESSFUL DM starts the cooldown — a failed send must
1314
- # be retried next tick, not suppressed.
1386
+ if _dm_wake(gateway, self_peer, target, token or "", _DM_WAKE_PROMPT) == "accepted":
1387
+ # Gateway stored the row. Stamp the cooldown so the next tick
1388
+ # does not re-POST, but do NOT count it as a fired wake: a 2xx
1389
+ # is not the recipient's ledger (#184). A rejected POST leaves
1390
+ # the cooldown unset so the next tick retries.
1315
1391
  state[target] = now_epoch
1316
- fired += 1
1392
+ accepted += 1
1317
1393
 
1318
1394
  # Persist the (possibly-updated) cooldown map once after the loop.
1319
1395
  _save_dm_wake_state(state_path, state)
@@ -1325,12 +1401,13 @@ def _dm_wake_scan(
1325
1401
  "self_peer": self_peer,
1326
1402
  "stale_peers": stale,
1327
1403
  "skipped_cooldown": skipped,
1328
- "wakes_fired": fired,
1404
+ "wakes_accepted": accepted,
1405
+ "wakes_fired": 0,
1329
1406
  "cooldown_sec": cooldown_sec,
1330
1407
  },
1331
1408
  args.verbose,
1332
1409
  )
1333
- return fired
1410
+ return 0
1334
1411
 
1335
1412
 
1336
1413
  def run_check(args: argparse.Namespace) -> int:
@@ -1338,13 +1415,11 @@ def run_check(args: argparse.Namespace) -> int:
1338
1415
  exit 0/1/2/3/4 — byte-for-byte unchanged), then layers the ``--dm-wake``
1339
1416
  mesh-monitor scan on top.
1340
1417
 
1341
- Exit precedence (T3): the local own-session action wins. If the local
1342
- decision took a real action or errored (rc != 0), that code is returned
1343
- unchanged — the dm-wake scan is an ADDITIONAL mesh-monitor action and
1344
- must not suppress or be suppressed by the local signal. ONLY when the
1345
- local decision was a no-op (rc == 0) AND at least one cross-host wake DM
1346
- fired do we surface exit 3 (A1-DM). When ``--dm-wake`` is off the scan is
1347
- a pure no-op, so the 0/1/2 behavior is preserved exactly.
1418
+ Exit precedence (T3): the local own-session action wins. The dm-wake
1419
+ scan POSTs and returns 0: a gateway accept is not a delivered wake, so
1420
+ it does not surface exit 3 (#184). Exit 3 remains the local detection
1421
+ error (unreadable cursor). When ``--dm-wake`` is off the scan is a
1422
+ pure no-op.
1348
1423
  """
1349
1424
  log_path = _resolve_log_path(args.log)
1350
1425
  global _HEALTH_EMITTER
@@ -3117,7 +3117,7 @@ async def scheduled_event_fire_now(name: str,
3117
3117
  c.execute(
3118
3118
  "UPDATE scheduled_events SET last_fired_at = ?, last_status = 'fired', "
3119
3119
  "fire_count = fire_count + 1 WHERE name = ?", (now, name))
3120
- return {"name": name, "fired": True, "fired_at": now,
3120
+ return {"name": name, "fired": False, "marked": True, "fired_at": now,
3121
3121
  "target_cell": row["target_cell"], "task": row["task"],
3122
3122
  "context_ref": json.loads(row["context_ref"])}
3123
3123
 
@@ -16,11 +16,28 @@ from typing import Iterable
16
16
  GAP_SECONDS = 60
17
17
 
18
18
 
19
+ def _channel_alive(inbox: str, now: float | None = None) -> bool:
20
+ """A channel server writes channel_heartbeat.json beside the inbox every 60 s.
21
+
22
+ `swarph channel-serve` takes no inbox argument, so no command line names the
23
+ path; a fresh heartbeat is the only evidence the channel is reading.
24
+ """
25
+ hb = Path(inbox).parent / "channel_heartbeat.json"
26
+ try:
27
+ ts = float(json.loads(hb.read_text(encoding="utf-8")).get("ts") or 0)
28
+ except (OSError, ValueError, TypeError, AttributeError):
29
+ return False
30
+ return (time.time() if now is None else now) - ts < 180
31
+
32
+
19
33
  def _watching(inbox: str, cmdlines: Iterable[str]) -> bool:
20
- """A reader is tail or dm_notify_filter with the inbox path as its own argument.
34
+ """A reader is tail or dm_notify_filter with the inbox path as its own argument,
35
+ or a channel server whose heartbeat is fresh.
21
36
 
22
37
  A process that merely mentions the path (a prompt, a log line) is not a reader.
23
38
  """
39
+ if _channel_alive(inbox):
40
+ return True
24
41
  for cmd in cmdlines:
25
42
  parts = cmd.split()
26
43
  if inbox not in parts:
@@ -29,15 +46,6 @@ def _watching(inbox: str, cmdlines: Iterable[str]) -> bool:
29
46
  return True
30
47
  if any(p.endswith("dm_notify_filter") or p.endswith("dm_notify_filter.py") for p in parts):
31
48
  return True
32
- if any(p.endswith("swarph_cli.channel") or p.endswith("channel.py") for p in parts):
33
- hb = Path(inbox).parent / "channel_heartbeat.json"
34
- try:
35
- data = json.loads(hb.read_text(encoding="utf-8"))
36
- ts = float(data.get("ts") or 0)
37
- except (OSError, ValueError, TypeError):
38
- continue
39
- if time.time() - ts < 180:
40
- return True
41
49
  return False
42
50
 
43
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.68.0
3
+ Version: 0.68.2
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
@@ -182,6 +182,7 @@ tests/test_132_ensure_monitor_state_dir.py
182
182
  tests/test_138_memory_navigate.py
183
183
  tests/test_139_tool_probe.py
184
184
  tests/test_183_card_anchor.py
185
+ tests/test_184_notify_honesty.py
185
186
  tests/test_201_win32_bash_refusal.py
186
187
  tests/test_243_onboard_per_peer_token.py
187
188
  tests/test_307_task4_ask_reply.py
@@ -227,6 +228,7 @@ tests/test_764_enrich_collapse.py
227
228
  tests/test_801_confirm_flow_cli.py
228
229
  tests/test_802_stage_history.py
229
230
  tests/test_825_codegraph_hook_noise.py
231
+ tests/test_825_install_posttooluse_only.py
230
232
  tests/test_825_remaining_cap_shred_bind.py
231
233
  tests/test_829_counterfactual_column.py
232
234
  tests/test_830_hook_binding_version.py