swarph-cli 0.72.5__tar.gz → 0.72.7__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 (478) hide show
  1. {swarph_cli-0.72.5/src/swarph_cli.egg-info → swarph_cli-0.72.7}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/mesh.py +35 -17
  5. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/dm_notify_filter.py +33 -10
  6. {swarph_cli-0.72.5 → swarph_cli-0.72.7/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  7. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
  8. swarph_cli-0.72.7/tests/test_1050_surrogate_wake.py +115 -0
  9. swarph_cli-0.72.7/tests/test_1052_opencode_deaf.py +105 -0
  10. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/LICENSE +0 -0
  11. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/README.md +0 -0
  12. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/setup.cfg +0 -0
  13. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/__main__.py +0 -0
  14. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/__init__.py +0 -0
  15. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/backends.py +0 -0
  16. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/claude_cli.py +0 -0
  17. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/codex_cli.py +0 -0
  18. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/pack.py +0 -0
  19. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/prices.py +0 -0
  20. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/providers.py +0 -0
  21. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/quality.py +0 -0
  22. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/refresh_prices.py +0 -0
  23. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/rules.py +0 -0
  24. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/runner.py +0 -0
  25. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/signal_score.py +0 -0
  26. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/bench/validate.py +0 -0
  27. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/caller.py +0 -0
  28. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/__init__.py +0 -0
  29. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/harden.py +0 -0
  30. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/lineage.py +0 -0
  31. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/liveness.py +0 -0
  32. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/manifest.py +0 -0
  33. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/paths.py +0 -0
  34. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/capture/verify.py +0 -0
  35. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/cell.py +0 -0
  36. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/chain_token.py +0 -0
  37. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/channel.py +0 -0
  38. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/__init__.py +0 -0
  39. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/_content.py +0 -0
  40. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/_display.py +0 -0
  41. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/_gateway_client.py +0 -0
  42. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/add.py +0 -0
  43. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/bench.py +0 -0
  44. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/board.py +0 -0
  45. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/board_merge_check.py +0 -0
  46. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/brain.py +0 -0
  47. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/brain_ask.py +0 -0
  48. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/cell.py +0 -0
  49. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/cell_probe.py +0 -0
  50. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  51. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/channel.py +0 -0
  52. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/chat.py +0 -0
  53. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/codegraph.py +0 -0
  54. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  55. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  56. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/codex_waker.py +0 -0
  57. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/compress.py +0 -0
  58. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/daemon.py +0 -0
  59. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/dreaming.py +0 -0
  60. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/event.py +0 -0
  61. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/gateway.py +0 -0
  62. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/gh_route.py +0 -0
  63. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/group.py +0 -0
  64. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/guide.py +0 -0
  65. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/highlight.py +0 -0
  66. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  67. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/hook_output.py +0 -0
  68. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/hooks.py +0 -0
  69. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/import_session.py +0 -0
  70. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/init.py +0 -0
  71. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  72. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_hook.py +0 -0
  73. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  74. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  75. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  76. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  77. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/lane.py +0 -0
  78. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/mcp_server.py +0 -0
  79. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/memory.py +0 -0
  80. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  81. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/memory_sync.py +0 -0
  82. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/monitor.py +0 -0
  83. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/okf_links.py +0 -0
  84. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/onboard.py +0 -0
  85. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  86. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  87. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/protocol_handler.py +0 -0
  88. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/ratify.py +0 -0
  89. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  90. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/schedule.py +0 -0
  91. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/security.py +0 -0
  92. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/service.py +0 -0
  93. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/spawn.py +0 -0
  94. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/timeline.py +0 -0
  95. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/version.py +0 -0
  96. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  97. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/commands/watchdog.py +0 -0
  98. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/compress/__init__.py +0 -0
  99. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/compress/levers.py +0 -0
  100. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/compress/marker.py +0 -0
  101. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/compress/verify.py +0 -0
  102. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/console_safe.py +0 -0
  103. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/delivery_queue.py +0 -0
  104. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dm_frame.py +0 -0
  105. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/__init__.py +0 -0
  106. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/candidates.py +0 -0
  107. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/clone.py +0 -0
  108. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/enrich.py +0 -0
  109. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/index_check.py +0 -0
  110. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/notify.py +0 -0
  111. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/organize.py +0 -0
  112. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/probes.py +0 -0
  113. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/report.py +0 -0
  114. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/run.py +0 -0
  115. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/slm.py +0 -0
  116. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/transcripts.py +0 -0
  117. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/dreaming/verify.py +0 -0
  118. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/__init__.py +0 -0
  119. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/feature_registry.py +0 -0
  120. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/lanes_control.py +0 -0
  121. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/schema.sql +0 -0
  122. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/server.py +0 -0
  123. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway/services_control.py +0 -0
  124. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gateway_default.py +0 -0
  125. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/gh_identity.py +0 -0
  126. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/guide/GUIDE.md +0 -0
  127. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/identity.py +0 -0
  128. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/main.py +0 -0
  129. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/multiplexer.py +0 -0
  130. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/__init__.py +0 -0
  131. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/__main__.py +0 -0
  132. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/api.py +0 -0
  133. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/cli.py +0 -0
  134. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/config.py +0 -0
  135. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/operator/fmt.py +0 -0
  136. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/orphan_daemons.py +0 -0
  137. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/pane_probe.py +0 -0
  138. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/parsers/__init__.py +0 -0
  139. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/parsers/claude.py +0 -0
  140. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  141. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  142. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  143. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  144. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  145. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  146. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  147. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  148. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  149. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  150. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  151. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  152. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_executor.py +0 -0
  153. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_executor_windows.py +0 -0
  154. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_reconciliation.py +0 -0
  155. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_reply_outbox.py +0 -0
  156. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_service_host.py +0 -0
  157. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_service_payload.py +0 -0
  158. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_service_reply.py +0 -0
  159. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/peer_staging.py +0 -0
  160. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/query_filters.py +0 -0
  161. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/rowclear.py +0 -0
  162. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/__init__.py +0 -0
  163. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  164. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  165. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  166. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  167. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/service/__init__.py +0 -0
  168. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/service/app.py +0 -0
  169. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/service/providers.py +0 -0
  170. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/session_bridge.py +0 -0
  171. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  172. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  173. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/source_text.py +0 -0
  174. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/stall_alert.py +0 -0
  175. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/codex-waker.default +0 -0
  176. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  177. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  178. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  179. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  180. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  181. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  182. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  183. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  184. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  185. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  186. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  187. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  188. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  189. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/timeline_paths.py +0 -0
  190. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli/tokens.py +0 -0
  191. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  192. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  193. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli.egg-info/requires.txt +0 -0
  194. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/src/swarph_cli.egg-info/top_level.txt +0 -0
  195. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_1006_board_publisher.py +0 -0
  196. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_1006_session_board.py +0 -0
  197. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_1010_strand_only.py +0 -0
  198. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_1013_spawn_channel.py +0 -0
  199. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_1017_muse_tui.py +0 -0
  200. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_124_register_merge.py +0 -0
  201. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_126_pending_from_complete.py +0 -0
  202. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  203. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_138_memory_navigate.py +0 -0
  204. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_139_tool_probe.py +0 -0
  205. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_183_card_anchor.py +0 -0
  206. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_184_notify_honesty.py +0 -0
  207. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_201_win32_bash_refusal.py +0 -0
  208. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_243_onboard_per_peer_token.py +0 -0
  209. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_307_task4_ask_reply.py +0 -0
  210. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  211. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_319_refusal_exit_codes.py +0 -0
  212. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_323_mistral_backend.py +0 -0
  213. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_332_report_organize_findings.py +0 -0
  214. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_356_query_filters.py +0 -0
  215. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_397_gh_identity_router.py +0 -0
  216. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_397_router_preflight.py +0 -0
  217. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_402_one_identity_resolver.py +0 -0
  218. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_432_rowclear.py +0 -0
  219. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_467b_onboard_operator_credential.py +0 -0
  220. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_472_register_caller_binding.py +0 -0
  221. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_484_codex_queue_sink.py +0 -0
  222. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_523_guide.py +0 -0
  223. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_524_conftest_hermeticity.py +0 -0
  224. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_532_accept_flag.py +0 -0
  225. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_535_version_capability.py +0 -0
  226. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_538_signal_bench.py +0 -0
  227. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_546_default_gateway.py +0 -0
  228. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_547_verbs_from_registry.py +0 -0
  229. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_548_gateway_remainder.py +0 -0
  230. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_552_codegraph_gateway.py +0 -0
  231. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_555_relay_row_shape.py +0 -0
  232. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_556_relay_freshness.py +0 -0
  233. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_559_edges_truncated.py +0 -0
  234. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_564_bootstrap_first_peer.py +0 -0
  235. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  236. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_579_register_bootstrap_message.py +0 -0
  237. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_591_cli_step_graph.py +0 -0
  238. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_644_monitor_supervision.py +0 -0
  239. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_648_install_unit.py +0 -0
  240. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_654_reexec.py +0 -0
  241. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_656_owned_slm.py +0 -0
  242. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_684_dreaming_verb.py +0 -0
  243. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_689_pkg_version_order.py +0 -0
  244. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_715_grok_wake.py +0 -0
  245. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_716_timeline_one_path.py +0 -0
  246. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_723_grok_0251.py +0 -0
  247. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  248. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_725_stdio_reconfigure.py +0 -0
  249. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_726_grok_limit_screen.py +0 -0
  250. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_729_arm_check_delivery_gap.py +0 -0
  251. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_729_dm_once.py +0 -0
  252. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_729_wake_watchdog.py +0 -0
  253. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  254. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_735_dm_mcp_tools.py +0 -0
  255. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_739_wake_writer.py +0 -0
  256. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_740_edit_project.py +0 -0
  257. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_762_cards_edit_priority.py +0 -0
  258. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_764_enrich_collapse.py +0 -0
  259. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_801_confirm_flow_cli.py +0 -0
  260. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_802_stage_history.py +0 -0
  261. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_825_codegraph_hook_noise.py +0 -0
  262. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_825_install_posttooluse_only.py +0 -0
  263. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  264. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_829_counterfactual_column.py +0 -0
  265. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_830_hook_binding_version.py +0 -0
  266. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_84_cli_labels.py +0 -0
  267. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_866_doctrine_guide.py +0 -0
  268. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_872_identity_refuses_not_warns.py +0 -0
  269. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_874_source_text.py +0 -0
  270. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  271. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_937_dreaming_notify.py +0 -0
  272. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_938_organize_line_limit.py +0 -0
  273. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_941_bench_backends.py +0 -0
  274. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_941_harness_342.py +0 -0
  275. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_945_same_line_pairing.py +0 -0
  276. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_955_prompt_audit.py +0 -0
  277. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_957_auth_http.py +0 -0
  278. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_959_venv_interpreter.py +0 -0
  279. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_960_channel.py +0 -0
  280. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  281. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_961_cursor_grok_footer.py +0 -0
  282. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_961_loud_stop.py +0 -0
  283. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_963_plugin_manifest.py +0 -0
  284. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_964_channel_cell.py +0 -0
  285. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_965_mesh_wait.py +0 -0
  286. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_975_channel_hop.py +0 -0
  287. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_add_security_gate.py +0 -0
  288. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_add.py +0 -0
  289. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_hash.py +0 -0
  290. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_lib.py +0 -0
  291. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_mcp.py +0 -0
  292. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_skill.py +0 -0
  293. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_tool.py +0 -0
  294. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_artifact_uri.py +0 -0
  295. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_backends.py +0 -0
  296. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_claude_cli.py +0 -0
  297. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_codex_cli.py +0 -0
  298. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_command.py +0 -0
  299. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_pack.py +0 -0
  300. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_prices.py +0 -0
  301. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_quality.py +0 -0
  302. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_runner.py +0 -0
  303. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_bench_validate.py +0 -0
  304. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_board_card_thread.py +0 -0
  305. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_board_command.py +0 -0
  306. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_board_merge_check.py +0 -0
  307. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_board_obligation_close.py +0 -0
  308. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_brain_ask_command.py +0 -0
  309. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_brain_ask_env_diagnosis.py +0 -0
  310. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_brain_ask_identity_explicitness.py +0 -0
  311. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_brain_command.py +0 -0
  312. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_caller_meta_guard.py +0 -0
  313. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_capture_lineage.py +0 -0
  314. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_capture_liveness.py +0 -0
  315. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_capture_manifest.py +0 -0
  316. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_capture_paths.py +0 -0
  317. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_capture_security.py +0 -0
  318. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cell_command_dispatch.py +0 -0
  319. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cell_harden.py +0 -0
  320. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cell_loader.py +0 -0
  321. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cell_selfcheck.py +0 -0
  322. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cell_verify.py +0 -0
  323. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_chain_token.py +0 -0
  324. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_channel_command.py +0 -0
  325. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  326. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_channel_post.py +0 -0
  327. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_channel_wake_policy_filter.py +0 -0
  328. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_chat_command.py +0 -0
  329. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_claude_parser.py +0 -0
  330. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_claude_tmux_template.py +0 -0
  331. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_codegraph_caller_scope.py +0 -0
  332. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_codegraph_command.py +0 -0
  333. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_codegraph_hook.py +0 -0
  334. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_codegraph_mcp.py +0 -0
  335. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_codex_waker.py +0 -0
  336. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_compress_caller_convention.py +0 -0
  337. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_compress_command.py +0 -0
  338. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_compress_levers.py +0 -0
  339. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_compress_marker.py +0 -0
  340. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_compress_verify.py +0 -0
  341. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_console_encoding_e2e.py +0 -0
  342. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_content_composition.py +0 -0
  343. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_cursor_membrane.py +0 -0
  344. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_daemon_command.py +0 -0
  345. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_daemon_console_encoding.py +0 -0
  346. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_daemon_delivery.py +0 -0
  347. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_delivery_queue.py +0 -0
  348. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_display_sanitize.py +0 -0
  349. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  350. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_event_emit.py +0 -0
  351. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_feature_to_uri.py +0 -0
  352. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_gateway_brain.py +0 -0
  353. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_gateway_client.py +0 -0
  354. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_gateway_command.py +0 -0
  355. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_gateway_message_idempotency.py +0 -0
  356. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  357. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_group_command.py +0 -0
  358. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_guide_commands_parse.py +0 -0
  359. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_guide_selfcheck.py +0 -0
  360. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_heartbeat_check.py +0 -0
  361. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_highlight_command.py +0 -0
  362. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hook_output.py +0 -0
  363. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_activity_marker.py +0 -0
  364. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_add.py +0 -0
  365. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_bundle.py +0 -0
  366. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_init.py +0 -0
  367. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_legacy_generation_removal.py +0 -0
  368. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_lifecycle.py +0 -0
  369. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_merge.py +0 -0
  370. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_verify.py +0 -0
  371. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  372. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_hooks_win32_command_path.py +0 -0
  373. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_import_command.py +0 -0
  374. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_inbox_identity_provenance.py +0 -0
  375. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_init_command.py +0 -0
  376. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_init_provider_menu.py +0 -0
  377. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_install_codex_hooks.py +0 -0
  378. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_install_hook.py +0 -0
  379. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_install_multiplexer_command.py +0 -0
  380. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_install_postcompact_hook.py +0 -0
  381. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_install_wake_hook.py +0 -0
  382. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_lane_command.py +0 -0
  383. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_main.py +0 -0
  384. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_main_unknown_verb_guard.py +0 -0
  385. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mcp_missing_hint.py +0 -0
  386. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mcp_server.py +0 -0
  387. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_memory_command.py +0 -0
  388. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_memory_emit_snippet.py +0 -0
  389. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_memory_gateway.py +0 -0
  390. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_memory_sync.py +0 -0
  391. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_memory_sync_membrane.py +0 -0
  392. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_command.py +0 -0
  393. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_inbox_mark_read.py +0 -0
  394. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_inbox_sanitize.py +0 -0
  395. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_originate_thread_623.py +0 -0
  396. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_send_unknown_peer.py +0 -0
  397. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_sidecar.py +0 -0
  398. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  399. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  400. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  401. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_meta_edge_identity.py +0 -0
  402. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_monitor_channel_poll.py +0 -0
  403. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_monitor_cli.py +0 -0
  404. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_monitor_daemon_collision.py +0 -0
  405. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_monitor_self_name_verification.py +0 -0
  406. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_monitor_state_split.py +0 -0
  407. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_multiplexer.py +0 -0
  408. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_muse_membrane.py +0 -0
  409. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_muse_own_cli.py +0 -0
  410. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_obligation_close_is_surfaced.py +0 -0
  411. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_okf_links.py +0 -0
  412. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_onboard_command.py +0 -0
  413. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_opencode_membrane.py +0 -0
  414. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_opencode_plugin.py +0 -0
  415. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_operator_me.py +0 -0
  416. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_orphan_daemons.py +0 -0
  417. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_packaged_artifacts_exist.py +0 -0
  418. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_packaged_skills.py +0 -0
  419. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_executor.py +0 -0
  420. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_executor_windows.py +0 -0
  421. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_reply_drain_command.py +0 -0
  422. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_reply_drain_supervisor.py +0 -0
  423. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_reply_outbox.py +0 -0
  424. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_reply_transport.py +0 -0
  425. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_service_e2e.py +0 -0
  426. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_service_host.py +0 -0
  427. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_service_payload.py +0 -0
  428. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_peer_service_reply.py +0 -0
  429. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_postcompact_recall.py +0 -0
  430. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_process_liveness.py +0 -0
  431. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_protocol_handler.py +0 -0
  432. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_providers_isolation.py +0 -0
  433. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_ratify_command.py +0 -0
  434. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_readme_claims_no_gateway_default.py +0 -0
  435. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_repl_caller_convention.py +0 -0
  436. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_schedule_command.py +0 -0
  437. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_schedule_create_contract.py +0 -0
  438. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_security.py +0 -0
  439. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_service_app.py +0 -0
  440. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_service_command.py +0 -0
  441. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_service_providers.py +0 -0
  442. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_session_bridge_inject.py +0 -0
  443. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_session_bridge_probe.py +0 -0
  444. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_session_bridge_resolve.py +0 -0
  445. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_session_path_role_gate.py +0 -0
  446. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_shipped_units_match_the_cli.py +0 -0
  447. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_smoke_chat.py +0 -0
  448. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_smoke_one_shot.py +0 -0
  449. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_smoke_phase_5_5.py +0 -0
  450. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_base_hoist.py +0 -0
  451. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_command.py +0 -0
  452. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_identity_stamp.py +0 -0
  453. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_live_pin.py +0 -0
  454. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_mitosis_lineage.py +0 -0
  455. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_tmux_session.py +0 -0
  456. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_spawn_windows_relaunch.py +0 -0
  457. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_stall_alert.py +0 -0
  458. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_subprocess_decode_encoding.py +0 -0
  459. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_timeline_command.py +0 -0
  460. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_tmux_wake_runstate_defer.py +0 -0
  461. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_tmux_wake_session_reanchor.py +0 -0
  462. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_tmux_wake_stale.py +0 -0
  463. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_tmux_wake_submit_verify.py +0 -0
  464. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_token_file_one_parser.py +0 -0
  465. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_token_precedence_explicit_first.py +0 -0
  466. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_unified_token_resolver.py +0 -0
  467. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_version_command.py +0 -0
  468. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_version_consistency.py +0 -0
  469. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_vibe_membrane.py +0 -0
  470. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog.py +0 -0
  471. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_dm_wake.py +0 -0
  472. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  473. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  474. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_emit_health.py +0 -0
  475. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_install_flags.py +0 -0
  476. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_model_rung.py +0 -0
  477. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_watchdog_stale_peers.py +0 -0
  478. {swarph_cli-0.72.5 → swarph_cli-0.72.7}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.5
3
+ Version: 0.72.7
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.72.5"
7
+ version = "0.72.7"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.72.5"
19
+ __version__ = "0.72.7"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -1515,7 +1515,14 @@ class TmuxSink(Sink):
1515
1515
  # Opencode keeps the finished ▣ header on screen, so a deferral
1516
1516
  # during the turn must not clear the flag: the next idle poll
1517
1517
  # still owes a wake. Cursor's #620 clear stays.
1518
- if _opencode_in_progress(self.target) or _grok_in_progress(self.target):
1518
+ if _opencode_in_progress(self.target):
1519
+ # Card #1052: name the real deferral cause — without this
1520
+ # the log says "composer holds human text" on a CLEAR
1521
+ # composer while a finished header misreads as live.
1522
+ self.deferred_reason = "opencode turn in progress"
1523
+ return None
1524
+ if _grok_in_progress(self.target):
1525
+ self.deferred_reason = "grok turn in progress"
1519
1526
  return None
1520
1527
  led["wake_outstanding"] = False
1521
1528
  if led.get("wake_outstanding"):
@@ -2327,10 +2334,15 @@ _MUSE_WAKE_PROMPT = "check mesh: swarph_dm_unread"
2327
2334
  # A finished turn keeps its ▣ header and adds a duration.
2328
2335
  # Short turns read `· 3.3s` (drop-on-meta-edge, 1.18.33, card #961 post
2329
2336
  # 54557). A longer idle turn reads `· 3m 4s` (opencode pane, 2026-09-29
2330
- # 07:10Z). On a wide pane the same row continues into the sidebar, so the
2331
- # duration is not the end of the line. An in-progress header has no duration.
2337
+ # 07:10Z). Long turns read h/m-only: `· 4h 59m`, `· 12m`, `· 1h` (opencode,
2338
+ # card #1052 — the old regex needed trailing seconds and read these as
2339
+ # live for 32h). On a wide pane the same row continues into the sidebar,
2340
+ # so the duration is not the end of the line. An in-progress header has
2341
+ # no duration.
2332
2342
  _OPENCODE_DONE = re.compile(
2333
- r"·\s*(?:\d+h\s*)?(?:\d+m\s*)?\d+(?:\.\d+)?s\b"
2343
+ r"·\s*(?:(?:\d+h\s+)?(?:\d+m\s+)?\d+(?:\.\d+)?s"
2344
+ r"|\d+h(?:\s+\d+m)?"
2345
+ r"|\d+m)\b"
2334
2346
  )
2335
2347
  # Submit-verify bounds (#533): the settle pause lets the -l literal LAND in
2336
2348
  # the composer before Enter can submit it (the blind gesture raced this), and
@@ -2504,31 +2516,37 @@ def _is_opencode_pane(lines: list[str]) -> bool:
2504
2516
  return bool(lines) and _bottom_tui(lines) == "opencode"
2505
2517
 
2506
2518
 
2519
+ def _last_box_header(lines: list[str]) -> Optional[str]:
2520
+ """The LAST ▣ header on screen. Scrollback keeps old headers (card
2521
+ #1052: a finished `4h 59m` header above the live tail); only the
2522
+ bottom-most one judges the turn."""
2523
+ for ln in reversed(lines):
2524
+ if ln.strip().startswith("▣"):
2525
+ return ln.strip()
2526
+ return None
2527
+
2528
+
2507
2529
  def _opencode_running(lines: list[str]) -> bool:
2508
2530
  """True only while a turn is in progress.
2509
2531
 
2510
- The footer shows ``esc interrupt``, and the ▣ header has no trailing
2511
- duration. A finished turn keeps the ▣ header and adds ``· <n>s``,
2512
- with no interrupt hint. That pane is idle. The permission dialog is
2532
+ The footer shows ``esc interrupt``, and the last ▣ header has no
2533
+ trailing duration. A finished turn keeps the ▣ header and adds a
2534
+ duration (``· <n>s`` or h/m-only like ``· 4h 59m``), with no
2535
+ interrupt hint. That pane is idle. The permission dialog is
2513
2536
  not a turn; it has its own branch.
2514
2537
  """
2515
2538
  if any("esc interrupt" in ln for ln in lines):
2516
2539
  return True
2517
- for ln in lines:
2518
- s = ln.strip()
2519
- if s.startswith("▣") and not _OPENCODE_DONE.search(s):
2520
- return True
2521
- return False
2540
+ last = _last_box_header(lines)
2541
+ return last is not None and not _OPENCODE_DONE.search(last)
2522
2542
 
2523
2543
 
2524
2544
  def _opencode_turn_finished(lines: list[str]) -> bool:
2525
- """The ▣ header carried a duration and the interrupt hint is gone."""
2545
+ """The last ▣ header carried a duration and the interrupt hint is gone."""
2526
2546
  if _opencode_running(lines):
2527
2547
  return False
2528
- return any(
2529
- ln.strip().startswith("▣") and _OPENCODE_DONE.search(ln.strip())
2530
- for ln in lines
2531
- )
2548
+ last = _last_box_header(lines)
2549
+ return last is not None and bool(_OPENCODE_DONE.search(last))
2532
2550
 
2533
2551
 
2534
2552
  def _opencode_in_progress(target: str) -> bool:
@@ -41,6 +41,12 @@ from typing import IO, Any, Optional
41
41
 
42
42
  _IDLE_DEFAULT_SECONDS = 1800
43
43
 
44
+ # Card #1050: ANY per-line failure is a logged skip, never a process death
45
+ # (a death in --once mode reads as "no DM arrived"). The notice keeps the
46
+ # [MESH DM prefix and goes to stdout so a downstream waker wakes on it;
47
+ # it is ASCII-only so it cannot itself raise on a hostile-errors stdout.
48
+ _SKIP_NOTICE = "[MESH DM] unrenderable line skipped"
49
+
44
50
 
45
51
  def _reader(stream: IO[str], q: "queue.Queue[Optional[str]]") -> None:
46
52
  """Daemon thread: blocking readline loop, None sentinel on EOF."""
@@ -110,11 +116,16 @@ def run_filter(
110
116
  except Exception:
111
117
  d = None
112
118
  if isinstance(d, dict):
113
- d = d.get("dm") or d
114
- rendered = _format_dm(d)
115
- if rendered is not None:
116
- print(rendered, file=stdout, flush=True)
117
- quiet = False
119
+ try:
120
+ d = d.get("dm") or d
121
+ rendered = _format_dm(d)
122
+ if rendered is not None:
123
+ print(rendered, file=stdout, flush=True)
124
+ quiet = False
125
+ except BrokenPipeError:
126
+ raise # a closed pipe stays loud (SIGPIPE contract)
127
+ except Exception:
128
+ print(_SKIP_NOTICE, file=stdout, flush=True)
118
129
  processed += 1
119
130
  if max_lines is not None and processed >= max_lines:
120
131
  return 0
@@ -148,11 +159,16 @@ def run_once(path: str, stdout: IO[str], *, timeout: float = 2.0) -> int:
148
159
  except Exception:
149
160
  continue
150
161
  if isinstance(d, dict):
151
- d = d.get("dm") or d
152
- rendered = _format_dm(d) if isinstance(d, dict) else None
153
- if rendered is not None:
154
- print(rendered, file=stdout, flush=True)
155
- return 0
162
+ try:
163
+ d = d.get("dm") or d
164
+ rendered = _format_dm(d) if isinstance(d, dict) else None
165
+ if rendered is not None:
166
+ print(rendered, file=stdout, flush=True)
167
+ return 0
168
+ except BrokenPipeError:
169
+ raise # a closed pipe stays loud (SIGPIPE contract)
170
+ except Exception:
171
+ print(_SKIP_NOTICE, file=stdout, flush=True)
156
172
  time.sleep(0.05)
157
173
  return 1
158
174
 
@@ -171,6 +187,13 @@ def main(argv: Optional[list[str]] = None) -> int:
171
187
  p.add_argument("--timeout", type=float, default=2.0,
172
188
  help="seconds --once waits for a new DM (default: %(default)s)")
173
189
  args = p.parse_args(argv)
190
+ try:
191
+ # Card #1050: lone surrogates in DM content must render (as ?) and
192
+ # continue, never kill the watch on a strict-errors stdout. Set
193
+ # before EITHER print path: --once and filter mode both emit DMs.
194
+ sys.stdout.reconfigure(errors="replace")
195
+ except Exception:
196
+ pass
174
197
  if args.once:
175
198
  if not args.inbox:
176
199
  print("dm_notify_filter --once needs --inbox", file=sys.stderr)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.5
3
+ Version: 0.72.7
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
@@ -193,6 +193,8 @@ tests/test_1006_session_board.py
193
193
  tests/test_1010_strand_only.py
194
194
  tests/test_1013_spawn_channel.py
195
195
  tests/test_1017_muse_tui.py
196
+ tests/test_1050_surrogate_wake.py
197
+ tests/test_1052_opencode_deaf.py
196
198
  tests/test_124_register_merge.py
197
199
  tests/test_126_pending_from_complete.py
198
200
  tests/test_132_ensure_monitor_state_dir.py
@@ -0,0 +1,115 @@
1
+ """#1050 — a DM whose content holds a lone surrogate escape must not kill
2
+ the wake filter.
3
+
4
+ Found by drop-on-meta-edge in #1298 (card #1049 Go A/B): a valid JSON line
5
+ carrying e.g. \\ud83d makes the filter raise UnicodeEncodeError on print —
6
+ exit 1, NO deaf-watch line, and every later DM is never surfaced. This is
7
+ the fleet's silent-wake filter (#482): any peer's DM can deafen a cell's
8
+ wake without the deaf alarm firing.
9
+
10
+ The escape MUST be outside Python's surrogateescape round-trip range
11
+ (DC80-DCFF): \\udcff survives on UTF-8-mode boxes (it round-trips raw
12
+ byte 0xFF through a pipe) and only kills on strict-errors stdouts, while
13
+ \\ud83d (lead) and \\udfff (trail, above DCFF) kill everywhere — measured
14
+ dead on the pre-fix filter under both default and strict stdouts (#1302).
15
+
16
+ Seam: the process boundary — stdin lines in, stdout lines out — exactly
17
+ what the accept check names.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import subprocess
23
+ import sys
24
+
25
+ import pytest
26
+
27
+ MOD = [sys.executable, "-u", "-m", "swarph_cli.scripts.dm_notify_filter"]
28
+
29
+ # Lethal on every box (see module docstring for why these, not \\udcff).
30
+ LETHAL_ESCAPES = ("\\ud83d", "\\udfff")
31
+
32
+ GOOD = '{"id":12,"from_node":"b","kind":"fyi","content":"after bad"}'
33
+
34
+
35
+ def _run(payload: bytes) -> "subprocess.CompletedProcess[bytes]":
36
+ return subprocess.run(
37
+ MOD + ["--idle-seconds", "3600"],
38
+ input=payload,
39
+ capture_output=True,
40
+ timeout=60,
41
+ env=dict(os.environ),
42
+ )
43
+
44
+
45
+ def _bad(mid: int, esc: str) -> str:
46
+ return ('{"id":%d,"from_node":"a","kind":"fyi","content":"AB%scCD"}'
47
+ % (mid, esc))
48
+
49
+
50
+ @pytest.mark.parametrize("esc", LETHAL_ESCAPES)
51
+ def test_lone_surrogate_then_good_dm_survives(esc: str):
52
+ """The good DM after a lone-surrogate line is printed; the process
53
+ keeps running to EOF (deaf line) instead of dying on the bad line."""
54
+ p = _run((_bad(11, esc) + "\n" + GOOD + "\n").encode("ascii"))
55
+ assert b"id=12" in p.stdout, (
56
+ f"good DM lost after lone-surrogate line: rc={p.returncode} "
57
+ f"stdout={p.stdout!r} stderr={p.stderr[-300:]!r}"
58
+ )
59
+ assert b"DEAF" in p.stdout, (
60
+ f"watch died without its deaf-watch line: rc={p.returncode} "
61
+ f"stderr={p.stderr[-300:]!r}"
62
+ )
63
+
64
+
65
+ def test_lone_surrogate_never_kills_process():
66
+ """No per-line content may exit the filter: a whole stream of hostile
67
+ lines still ends at the EOF deaf line, not a traceback."""
68
+ lines = ([_bad(i, LETHAL_ESCAPES[i % 2]) for i in range(20)]
69
+ + [GOOD])
70
+ p = _run(("\n".join(lines) + "\n").encode("ascii"))
71
+ assert b"id=12" in p.stdout
72
+ assert b"DEAF" in p.stdout
73
+ assert b"Traceback" not in p.stderr
74
+
75
+
76
+ def test_once_skips_non_string_content_and_wakes_on_next(tmp_path):
77
+ """#1310: run_once shares the never-die contract. A first-arriving DM
78
+ whose content is not a string (a number here) must become a logged
79
+ skip, not a death read as 'no DM arrived'; the next good DM still
80
+ wakes (exit 0). Seam: the --once process boundary, fed live."""
81
+ import threading
82
+ import time
83
+
84
+ inbox = tmp_path / "inbox.log"
85
+ inbox.write_text("")
86
+ proc = subprocess.Popen(
87
+ MOD + ["--once", "--inbox", str(inbox), "--timeout", "10"],
88
+ stdout=subprocess.PIPE,
89
+ stderr=subprocess.PIPE,
90
+ env=dict(os.environ),
91
+ )
92
+
93
+ def feed():
94
+ time.sleep(0.5)
95
+ with open(inbox, "a") as fh:
96
+ fh.write('{"id":41,"from_node":"a","kind":"fyi",'
97
+ '"content":42}\n')
98
+ time.sleep(0.5)
99
+ with open(inbox, "a") as fh:
100
+ fh.write(GOOD + "\n")
101
+
102
+ threading.Thread(target=feed, daemon=True).start()
103
+ out, err = proc.communicate(timeout=30)
104
+ assert b"Traceback" not in err, f"run_once died: {err[-300:]!r}"
105
+ assert b"unrenderable line skipped" in out, (
106
+ f"numeric content was not a logged skip: rc={proc.returncode} "
107
+ f"stdout={out!r}"
108
+ )
109
+ assert b"id=12" in out, (
110
+ f"good DM after the skip did not wake: rc={proc.returncode} "
111
+ f"stdout={out!r}"
112
+ )
113
+ assert proc.returncode == 0, (
114
+ f"wake on the good DM must exit 0, got {proc.returncode}"
115
+ )
@@ -0,0 +1,105 @@
1
+ """#1052 — opencode wake DEAF 32h: _opencode_running read a FINISHED
2
+ '▣ ... · 4h 59m' header as a live turn (the done-regex needs trailing
3
+ seconds), so the monitor deferred forever, logging 'composer holds human
4
+ text' on a CLEAR composer.
5
+
6
+ Seams: the pure helpers on the two real captures (lab-ovh's 01:31Z and
7
+ 17:18Z fixtures), plus the TmuxSink deferral rigged at the deliver()
8
+ boundary exactly like test_1010_strand_only's. Items: (1) the done-regex
9
+ accepts h/m-only durations; (2) only the LAST ▣ header judges; (3) the
10
+ opencode-in-progress deferral names its real reason.
11
+
12
+ The committed fixtures are the real captures with one customer
13
+ identifier redacted (a 6-letter tenant prefix -> acme, 7 spots each);
14
+ headers, composer state and control lines are byte-identical to the
15
+ originals.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import pathlib
20
+
21
+ import pytest
22
+
23
+ import swarph_cli.commands.mesh as mesh
24
+
25
+ FIX = pathlib.Path(__file__).resolve().parent / "fixtures"
26
+
27
+
28
+ def _lines(name: str) -> list[str]:
29
+ return (FIX / name).read_text(encoding="utf-8").splitlines()
30
+
31
+
32
+ CAPTURES = ("opencode-deaf-0131Z.txt", "opencode-deaf-1718Z.txt")
33
+
34
+
35
+ @pytest.mark.parametrize("name", CAPTURES)
36
+ def test_finished_captures_are_not_running(name: str):
37
+ """Both real captures: the old scrollback '4h 59m' header is finished
38
+ and the newest '16.5s' header is finished — no live turn anywhere."""
39
+ lines = _lines(name)
40
+ assert any("4h 59m" in ln for ln in lines), "capture must hold the old header"
41
+ assert mesh._opencode_running(lines) is False
42
+ assert mesh._opencode_turn_finished(lines) is True
43
+
44
+
45
+ def test_bare_header_without_duration_still_running():
46
+ """A header with NO duration is a live turn (or a mid-render one) —
47
+ the last-header rule must not call it finished."""
48
+ assert mesh._opencode_running(["▣ Build · DeepSeek V4 Pro (New)"]) is True
49
+ assert mesh._opencode_turn_finished(
50
+ ["▣ Build · DeepSeek V4 Pro (New)"]) is False
51
+
52
+
53
+ def test_scrollback_done_does_not_mask_live_tail():
54
+ """Only the LAST ▣ header judges: a finished scrollback header plus a
55
+ live (duration-less) tail header is still a running turn."""
56
+ lines = ["▣ Build · x · 4h 59m", "some output",
57
+ "▣ Build · x (no duration yet)"]
58
+ assert mesh._opencode_running(lines) is True
59
+ assert mesh._opencode_turn_finished(lines) is False
60
+
61
+
62
+ @pytest.mark.parametrize("name", CAPTURES)
63
+ def test_h_m_only_durations_count_as_done(name: str):
64
+ """Item 1 directly: '4h 59m', '12m', '1h' all match the done-regex."""
65
+ assert mesh._OPENCODE_DONE.search("▣ Build · x · 4h 59m")
66
+ assert mesh._OPENCODE_DONE.search("▣ Build · x · 12m")
67
+ assert mesh._OPENCODE_DONE.search("▣ Build · x · 1h")
68
+ assert not mesh._OPENCODE_DONE.search("▣ Build · x (New)")
69
+
70
+
71
+ class _StubState:
72
+ def __init__(self):
73
+ self.gateway = "http://gw:8788"
74
+ self.self_name = "opencode"
75
+ self.token = "tok"
76
+ self._ledgers: dict = {}
77
+
78
+ def ledger(self, name: str) -> dict:
79
+ return self._ledgers.setdefault(
80
+ name, {"last_delivered_id": 0, "last_delivery_at": 0.0,
81
+ "consecutive_failures": 0})
82
+
83
+
84
+ def test_opencode_deferral_names_its_real_reason(monkeypatch):
85
+ """Item 3: the opencode-in-progress deferral sets deferred_reason, so
86
+ the monitor's DEFERRED line stops saying 'composer holds human text'.
87
+ Rigged at deliver(): wake outstanding, agent running, and the real
88
+ 17:18Z capture with a LIVE tail (bare header + interrupt hint — the
89
+ documented mid-turn screen) so the real reader judges in-progress."""
90
+ lines = _lines("opencode-deaf-1718Z.txt") + [
91
+ " ▣ Build · DeepSeek V4.1 Flash",
92
+ " esc interrupt",
93
+ ]
94
+ assert mesh._opencode_running(lines) is True, "rig must read live"
95
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: lines)
96
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: True)
97
+ state = _StubState()
98
+ led = state.ledger("tmux:opencode")
99
+ led["wake_outstanding"] = True
100
+ sink = mesh.TmuxSink("opencode")
101
+ assert sink.deliver(state, [{"id": 7}], 7) is None
102
+ assert "human text" not in (sink.deferred_reason or ""), \
103
+ sink.deferred_reason
104
+ assert "opencode" in (sink.deferred_reason or "").lower(), \
105
+ sink.deferred_reason
File without changes
File without changes
File without changes