swarph-cli 0.72.44__tar.gz → 0.72.45__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 (576) hide show
  1. {swarph_cli-0.72.44/src/swarph_cli.egg-info → swarph_cli-0.72.45}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/event.py +51 -4
  5. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/mcp_server.py +9 -3
  6. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/mesh.py +8 -1
  7. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/redact.py +6 -0
  8. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scrub.py +7 -0
  9. {swarph_cli-0.72.44 → swarph_cli-0.72.45/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  10. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
  11. swarph_cli-0.72.45/tests/test_1472_redact_boundaries.py +218 -0
  12. swarph_cli-0.72.45/tests/test_event_emit.py +166 -0
  13. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mcp_server.py +39 -0
  14. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_scrub.py +6 -0
  15. swarph_cli-0.72.44/tests/test_event_emit.py +0 -36
  16. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/LICENSE +0 -0
  17. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/README.md +0 -0
  18. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/setup.cfg +0 -0
  19. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/__main__.py +0 -0
  20. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/__init__.py +0 -0
  21. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/backends.py +0 -0
  22. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/claude_cli.py +0 -0
  23. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/codex_cli.py +0 -0
  24. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/pack.py +0 -0
  25. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/prices.py +0 -0
  26. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/providers.py +0 -0
  27. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/quality.py +0 -0
  28. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/refresh_prices.py +0 -0
  29. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/rules.py +0 -0
  30. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/runner.py +0 -0
  31. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/signal_score.py +0 -0
  32. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/validate.py +0 -0
  33. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/caller.py +0 -0
  34. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/__init__.py +0 -0
  35. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/harden.py +0 -0
  36. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/lineage.py +0 -0
  37. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/liveness.py +0 -0
  38. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/manifest.py +0 -0
  39. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/paths.py +0 -0
  40. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/verify.py +0 -0
  41. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/cell.py +0 -0
  42. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/chain_token.py +0 -0
  43. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/channel.py +0 -0
  44. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/__init__.py +0 -0
  45. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_content.py +0 -0
  46. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_display.py +0 -0
  47. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_gateway_client.py +0 -0
  48. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/add.py +0 -0
  49. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/bench.py +0 -0
  50. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board.py +0 -0
  51. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_cost.py +0 -0
  52. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_merge_check.py +0 -0
  53. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_usage.py +0 -0
  54. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain.py +0 -0
  55. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain_ask.py +0 -0
  56. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell.py +0 -0
  57. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_probe.py +0 -0
  58. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  59. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/channel.py +0 -0
  60. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/chat.py +0 -0
  61. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph.py +0 -0
  62. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  63. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  64. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_waker.py +0 -0
  65. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/commander.py +0 -0
  66. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/committee.py +0 -0
  67. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/compress.py +0 -0
  68. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/daemon.py +0 -0
  69. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/dev_extract.py +0 -0
  70. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/dreaming.py +0 -0
  71. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/gateway.py +0 -0
  72. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/gh_route.py +0 -0
  73. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/group.py +0 -0
  74. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/guide.py +0 -0
  75. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/highlight.py +0 -0
  76. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  77. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_output.py +0 -0
  78. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hooks.py +0 -0
  79. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/import_session.py +0 -0
  80. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/init.py +0 -0
  81. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  82. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_hook.py +0 -0
  83. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  84. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  85. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  86. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  87. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/lane.py +0 -0
  88. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory.py +0 -0
  89. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  90. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_sync.py +0 -0
  91. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/monitor.py +0 -0
  92. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/okf_links.py +0 -0
  93. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/onboard.py +0 -0
  94. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  95. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  96. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/protocol_handler.py +0 -0
  97. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/ratify.py +0 -0
  98. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/roster.py +0 -0
  99. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  100. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/schedule.py +0 -0
  101. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/security.py +0 -0
  102. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/service.py +0 -0
  103. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/skunkworks.py +0 -0
  104. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/spawn.py +0 -0
  105. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/sweep.py +0 -0
  106. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/timeline.py +0 -0
  107. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/triage.py +0 -0
  108. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/version.py +0 -0
  109. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  110. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/watchdog.py +0 -0
  111. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commit_trailers.py +0 -0
  112. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/__init__.py +0 -0
  113. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/levers.py +0 -0
  114. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/marker.py +0 -0
  115. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/verify.py +0 -0
  116. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/console_safe.py +0 -0
  117. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/delivery_queue.py +0 -0
  118. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dm_frame.py +0 -0
  119. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/__init__.py +0 -0
  120. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/candidates.py +0 -0
  121. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/clone.py +0 -0
  122. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/enrich.py +0 -0
  123. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/index_check.py +0 -0
  124. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/notify.py +0 -0
  125. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/organize.py +0 -0
  126. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/probes.py +0 -0
  127. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/report.py +0 -0
  128. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/run.py +0 -0
  129. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/slm.py +0 -0
  130. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/transcripts.py +0 -0
  131. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/verify.py +0 -0
  132. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/duplicate_session.py +0 -0
  133. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/__init__.py +0 -0
  134. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/feature_registry.py +0 -0
  135. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/lanes_control.py +0 -0
  136. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/schema.sql +0 -0
  137. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/server.py +0 -0
  138. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/services_control.py +0 -0
  139. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway_default.py +0 -0
  140. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gh_identity.py +0 -0
  141. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/guide/GUIDE.md +0 -0
  142. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/identity.py +0 -0
  143. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  144. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  145. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  146. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  147. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  148. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd_render.py +0 -0
  149. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/main.py +0 -0
  150. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/multiplexer.py +0 -0
  151. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/__init__.py +0 -0
  152. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/__main__.py +0 -0
  153. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/api.py +0 -0
  154. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/cli.py +0 -0
  155. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/config.py +0 -0
  156. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/fmt.py +0 -0
  157. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/orphan_daemons.py +0 -0
  158. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/pane_probe.py +0 -0
  159. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/parsers/__init__.py +0 -0
  160. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/parsers/claude.py +0 -0
  161. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  162. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  163. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  164. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  165. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  166. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  167. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  168. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  169. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  170. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  171. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  172. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  173. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor.py +0 -0
  174. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor_windows.py +0 -0
  175. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_reconciliation.py +0 -0
  176. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_reply_outbox.py +0 -0
  177. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_host.py +0 -0
  178. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_payload.py +0 -0
  179. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_reply.py +0 -0
  180. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_staging.py +0 -0
  181. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/query_filters.py +0 -0
  182. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/quota_sync.py +0 -0
  183. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/rowclear.py +0 -0
  184. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/__init__.py +0 -0
  185. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  186. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  187. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  188. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  189. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  190. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/__init__.py +0 -0
  191. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/app.py +0 -0
  192. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/providers.py +0 -0
  193. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/session_bridge.py +0 -0
  194. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  195. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  196. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/source_text.py +0 -0
  197. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/stall_alert.py +0 -0
  198. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/codex-waker.default +0 -0
  199. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  200. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  201. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  202. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  203. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  204. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  205. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  206. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  207. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  208. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  209. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  210. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  211. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  212. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  213. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  214. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  215. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  216. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/the_ten.py +0 -0
  217. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/timeline_paths.py +0 -0
  218. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/tokens.py +0 -0
  219. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/__init__.py +0 -0
  220. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/backend.py +0 -0
  221. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/job.py +0 -0
  222. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/owners.json +0 -0
  223. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/router.py +0 -0
  224. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/rows.py +0 -0
  225. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/sensor.py +0 -0
  226. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  227. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  228. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/requires.txt +0 -0
  229. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/top_level.txt +0 -0
  230. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1006_board_publisher.py +0 -0
  231. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1006_session_board.py +0 -0
  232. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1010_strand_only.py +0 -0
  233. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1013_spawn_channel.py +0 -0
  234. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1017_muse_tui.py +0 -0
  235. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1034_copy_mode.py +0 -0
  236. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1050_surrogate_wake.py +0 -0
  237. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1052_opencode_deaf.py +0 -0
  238. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1059_watchdog_unread_env.py +0 -0
  239. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1061_roster_cli.py +0 -0
  240. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1064_stuck_sweep.py +0 -0
  241. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1065_release.py +0 -0
  242. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1066_quiet_wakes.py +0 -0
  243. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1068_structured_accept_cli.py +0 -0
  244. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1071_quota_sync.py +0 -0
  245. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_triage.py +0 -0
  246. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_triage_board.py +0 -0
  247. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_wheel_install.py +0 -0
  248. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1075_task_cli.py +0 -0
  249. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1083_openai_decisions.py +0 -0
  250. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1144_cell_yaml_github.py +0 -0
  251. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1197_commander_taps.py +0 -0
  252. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1203_swarph_me_refusal.py +0 -0
  253. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1206_offer_question.py +0 -0
  254. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1214_egress.py +0 -0
  255. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1219_egress_probe.py +0 -0
  256. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1221_quota_windows.py +0 -0
  257. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1222_maybe_start_cap.py +0 -0
  258. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1228_quota_reach.py +0 -0
  259. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1243_the_ten.py +0 -0
  260. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1248_ship_price_table.py +0 -0
  261. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_124_register_merge.py +0 -0
  262. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1250_price_cache.py +0 -0
  263. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1256_commander_wake.py +0 -0
  264. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1261_mcp_install.py +0 -0
  265. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1262_wake_followups.py +0 -0
  266. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1265_wake_note.py +0 -0
  267. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1269_triage_label.py +0 -0
  268. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_126_pending_from_complete.py +0 -0
  269. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1277_close_usage.py +0 -0
  270. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1278_dreaming_reach.py +0 -0
  271. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1286_dm_unread_cursor.py +0 -0
  272. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1297_commander.py +0 -0
  273. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1300_authorization_check.py +0 -0
  274. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1301_restart_skew.py +0 -0
  275. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1320_commit_trailers.py +0 -0
  276. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1321_layered_redaction.py +0 -0
  277. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  278. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1340_router_rollout.py +0 -0
  279. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1346_shell_string_gh.py +0 -0
  280. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1347_shell_depth_cap.py +0 -0
  281. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1353_obligation_release_cli.py +0 -0
  282. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1385_duplicate_session_argv.py +0 -0
  283. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_138_memory_navigate.py +0 -0
  284. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1396_dev_extract.py +0 -0
  285. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_139_tool_probe.py +0 -0
  286. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_basic.py +0 -0
  287. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_cli.py +0 -0
  288. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1427_membrane_workflow.py +0 -0
  289. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1436_dev_extract_freshness.py +0 -0
  290. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework1_engine.py +0 -0
  291. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework2_engine.py +0 -0
  292. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework3_engine.py +0 -0
  293. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_standing_wake_expiry.py +0 -0
  294. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1470_gh_router_fail_closed.py +0 -0
  295. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1478_triage_dm.py +0 -0
  296. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_183_card_anchor.py +0 -0
  297. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_184_notify_honesty.py +0 -0
  298. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_201_win32_bash_refusal.py +0 -0
  299. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_243_onboard_per_peer_token.py +0 -0
  300. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_307_task4_ask_reply.py +0 -0
  301. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  302. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_3130_sender.py +0 -0
  303. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_3150_identity_router_install.py +0 -0
  304. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_319_refusal_exit_codes.py +0 -0
  305. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_323_mistral_backend.py +0 -0
  306. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_332_report_organize_findings.py +0 -0
  307. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_356_query_filters.py +0 -0
  308. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_397_gh_identity_router.py +0 -0
  309. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_397_router_preflight.py +0 -0
  310. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_402_one_identity_resolver.py +0 -0
  311. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_432_rowclear.py +0 -0
  312. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_467b_onboard_operator_credential.py +0 -0
  313. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_472_register_caller_binding.py +0 -0
  314. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_484_codex_queue_sink.py +0 -0
  315. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_513_duplicate_session.py +0 -0
  316. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_523_guide.py +0 -0
  317. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_524_conftest_hermeticity.py +0 -0
  318. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_532_accept_flag.py +0 -0
  319. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_535_version_capability.py +0 -0
  320. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_538_signal_bench.py +0 -0
  321. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_546_default_gateway.py +0 -0
  322. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_547_verbs_from_registry.py +0 -0
  323. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_548_gateway_remainder.py +0 -0
  324. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_552_codegraph_gateway.py +0 -0
  325. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_555_relay_row_shape.py +0 -0
  326. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_556_relay_freshness.py +0 -0
  327. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_559_edges_truncated.py +0 -0
  328. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_564_bootstrap_first_peer.py +0 -0
  329. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  330. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_579_register_bootstrap_message.py +0 -0
  331. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_591_cli_step_graph.py +0 -0
  332. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_644_monitor_supervision.py +0 -0
  333. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_648_install_unit.py +0 -0
  334. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_654_reexec.py +0 -0
  335. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_656_owned_slm.py +0 -0
  336. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_684_dreaming_verb.py +0 -0
  337. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_689_pkg_version_order.py +0 -0
  338. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_715_grok_wake.py +0 -0
  339. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_716_timeline_one_path.py +0 -0
  340. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_723_grok_0251.py +0 -0
  341. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  342. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_725_stdio_reconfigure.py +0 -0
  343. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_726_grok_limit_screen.py +0 -0
  344. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_arm_check_delivery_gap.py +0 -0
  345. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_dm_once.py +0 -0
  346. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_wake_watchdog.py +0 -0
  347. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  348. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_735_dm_mcp_tools.py +0 -0
  349. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_739_wake_writer.py +0 -0
  350. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_740_edit_project.py +0 -0
  351. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_762_cards_edit_priority.py +0 -0
  352. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_764_enrich_collapse.py +0 -0
  353. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_801_confirm_flow_cli.py +0 -0
  354. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_802_stage_history.py +0 -0
  355. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_codegraph_hook_noise.py +0 -0
  356. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_install_posttooluse_only.py +0 -0
  357. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  358. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_829_counterfactual_column.py +0 -0
  359. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_830_hook_binding_version.py +0 -0
  360. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_84_cli_labels.py +0 -0
  361. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_866_doctrine_guide.py +0 -0
  362. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_872_identity_refuses_not_warns.py +0 -0
  363. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_874_source_text.py +0 -0
  364. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  365. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_931_board_search.py +0 -0
  366. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_935_deadline_clock.py +0 -0
  367. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_937_dreaming_notify.py +0 -0
  368. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_938_organize_line_limit.py +0 -0
  369. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_941_bench_backends.py +0 -0
  370. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_941_harness_342.py +0 -0
  371. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_945_same_line_pairing.py +0 -0
  372. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_955_prompt_audit.py +0 -0
  373. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_957_auth_http.py +0 -0
  374. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_959_venv_interpreter.py +0 -0
  375. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_960_channel.py +0 -0
  376. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  377. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_961_cursor_grok_footer.py +0 -0
  378. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_961_loud_stop.py +0 -0
  379. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_963_plugin_manifest.py +0 -0
  380. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_964_channel_cell.py +0 -0
  381. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_965_mesh_wait.py +0 -0
  382. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_975_channel_hop.py +0 -0
  383. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_add_security_gate.py +0 -0
  384. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_add.py +0 -0
  385. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_hash.py +0 -0
  386. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_lib.py +0 -0
  387. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_mcp.py +0 -0
  388. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_skill.py +0 -0
  389. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_tool.py +0 -0
  390. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_uri.py +0 -0
  391. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_backends.py +0 -0
  392. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_claude_cli.py +0 -0
  393. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_codex_cli.py +0 -0
  394. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_command.py +0 -0
  395. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_pack.py +0 -0
  396. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_prices.py +0 -0
  397. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_quality.py +0 -0
  398. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_runner.py +0 -0
  399. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_validate.py +0 -0
  400. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_card_thread.py +0 -0
  401. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_command.py +0 -0
  402. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_merge_check.py +0 -0
  403. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_obligation_close.py +0 -0
  404. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_command.py +0 -0
  405. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_env_diagnosis.py +0 -0
  406. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_identity_explicitness.py +0 -0
  407. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_command.py +0 -0
  408. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_caller_meta_guard.py +0 -0
  409. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_lineage.py +0 -0
  410. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_liveness.py +0 -0
  411. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_manifest.py +0 -0
  412. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_paths.py +0 -0
  413. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_security.py +0 -0
  414. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_command_dispatch.py +0 -0
  415. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_harden.py +0 -0
  416. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_loader.py +0 -0
  417. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_selfcheck.py +0 -0
  418. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_verify.py +0 -0
  419. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_chain_token.py +0 -0
  420. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_command.py +0 -0
  421. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  422. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_post.py +0 -0
  423. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_wake_policy_filter.py +0 -0
  424. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_chat_command.py +0 -0
  425. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_claude_parser.py +0 -0
  426. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_claude_tmux_template.py +0 -0
  427. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_caller_scope.py +0 -0
  428. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_command.py +0 -0
  429. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_hook.py +0 -0
  430. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_mcp.py +0 -0
  431. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codex_waker.py +0 -0
  432. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_committee_command.py +0 -0
  433. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_caller_convention.py +0 -0
  434. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_command.py +0 -0
  435. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_levers.py +0 -0
  436. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_marker.py +0 -0
  437. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_verify.py +0 -0
  438. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_console_encoding_e2e.py +0 -0
  439. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_content_composition.py +0 -0
  440. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_copilot_membrane.py +0 -0
  441. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_copilot_pane_reader.py +0 -0
  442. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cursor_membrane.py +0 -0
  443. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_command.py +0 -0
  444. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_console_encoding.py +0 -0
  445. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_delivery.py +0 -0
  446. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_delivery_queue.py +0 -0
  447. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_display_sanitize.py +0 -0
  448. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  449. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_feature_to_uri.py +0 -0
  450. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_brain.py +0 -0
  451. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_client.py +0 -0
  452. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_command.py +0 -0
  453. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_message_idempotency.py +0 -0
  454. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  455. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_group_command.py +0 -0
  456. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_guide_commands_parse.py +0 -0
  457. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_guide_selfcheck.py +0 -0
  458. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_heartbeat_check.py +0 -0
  459. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_highlight_command.py +0 -0
  460. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hook_output.py +0 -0
  461. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_activity_marker.py +0 -0
  462. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_add.py +0 -0
  463. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_bundle.py +0 -0
  464. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_init.py +0 -0
  465. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_legacy_generation_removal.py +0 -0
  466. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_lifecycle.py +0 -0
  467. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_merge.py +0 -0
  468. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_verify.py +0 -0
  469. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  470. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_win32_command_path.py +0 -0
  471. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_import_command.py +0 -0
  472. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_inbox_identity_provenance.py +0 -0
  473. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_init_command.py +0 -0
  474. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_init_provider_menu.py +0 -0
  475. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_codex_hooks.py +0 -0
  476. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_hook.py +0 -0
  477. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_multiplexer_command.py +0 -0
  478. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_postcompact_hook.py +0 -0
  479. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_wake_hook.py +0 -0
  480. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_lane_command.py +0 -0
  481. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_launchd_units.py +0 -0
  482. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_main.py +0 -0
  483. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_main_unknown_verb_guard.py +0 -0
  484. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mcp_missing_hint.py +0 -0
  485. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_command.py +0 -0
  486. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_emit_snippet.py +0 -0
  487. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_gateway.py +0 -0
  488. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_sync.py +0 -0
  489. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_sync_membrane.py +0 -0
  490. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_command.py +0 -0
  491. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_inbox_mark_read.py +0 -0
  492. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_inbox_sanitize.py +0 -0
  493. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_originate_thread_623.py +0 -0
  494. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_send_unknown_peer.py +0 -0
  495. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar.py +0 -0
  496. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  497. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  498. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  499. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_meta_edge_identity.py +0 -0
  500. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_channel_poll.py +0 -0
  501. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_cli.py +0 -0
  502. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_daemon_collision.py +0 -0
  503. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_self_name_verification.py +0 -0
  504. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_state_split.py +0 -0
  505. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_multiplexer.py +0 -0
  506. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_muse_membrane.py +0 -0
  507. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_muse_own_cli.py +0 -0
  508. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_obligation_close_is_surfaced.py +0 -0
  509. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_okf_links.py +0 -0
  510. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_onboard_command.py +0 -0
  511. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_opencode_membrane.py +0 -0
  512. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_opencode_plugin.py +0 -0
  513. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_operator_me.py +0 -0
  514. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_orphan_daemons.py +0 -0
  515. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_packaged_artifacts_exist.py +0 -0
  516. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_packaged_skills.py +0 -0
  517. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_executor.py +0 -0
  518. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_executor_windows.py +0 -0
  519. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_command.py +0 -0
  520. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_supervisor.py +0 -0
  521. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_outbox.py +0 -0
  522. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_transport.py +0 -0
  523. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_e2e.py +0 -0
  524. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_host.py +0 -0
  525. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_payload.py +0 -0
  526. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_reply.py +0 -0
  527. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_postcompact_recall.py +0 -0
  528. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_process_liveness.py +0 -0
  529. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_protocol_handler.py +0 -0
  530. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_providers_isolation.py +0 -0
  531. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_ratify_command.py +0 -0
  532. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_readme_claims_no_gateway_default.py +0 -0
  533. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_repl_caller_convention.py +0 -0
  534. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_schedule_command.py +0 -0
  535. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_schedule_create_contract.py +0 -0
  536. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_security.py +0 -0
  537. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_app.py +0 -0
  538. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_command.py +0 -0
  539. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_providers.py +0 -0
  540. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_inject.py +0 -0
  541. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_probe.py +0 -0
  542. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_resolve.py +0 -0
  543. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_path_role_gate.py +0 -0
  544. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_shipped_units_match_the_cli.py +0 -0
  545. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_chat.py +0 -0
  546. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_one_shot.py +0 -0
  547. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_phase_5_5.py +0 -0
  548. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_base_hoist.py +0 -0
  549. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_command.py +0 -0
  550. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_identity_stamp.py +0 -0
  551. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_live_pin.py +0 -0
  552. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_mitosis_lineage.py +0 -0
  553. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_tmux_session.py +0 -0
  554. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_windows_relaunch.py +0 -0
  555. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_stall_alert.py +0 -0
  556. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_subprocess_decode_encoding.py +0 -0
  557. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_timeline_command.py +0 -0
  558. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_runstate_defer.py +0 -0
  559. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_session_reanchor.py +0 -0
  560. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_stale.py +0 -0
  561. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_submit_verify.py +0 -0
  562. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_token_file_one_parser.py +0 -0
  563. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_token_precedence_explicit_first.py +0 -0
  564. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_unified_token_resolver.py +0 -0
  565. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_version_command.py +0 -0
  566. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_version_consistency.py +0 -0
  567. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_vibe_membrane.py +0 -0
  568. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog.py +0 -0
  569. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake.py +0 -0
  570. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  571. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  572. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_emit_health.py +0 -0
  573. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_install_flags.py +0 -0
  574. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_model_rung.py +0 -0
  575. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_stale_peers.py +0 -0
  576. {swarph_cli-0.72.44 → swarph_cli-0.72.45}/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.44
3
+ Version: 0.72.45
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.44"
7
+ version = "0.72.45"
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.44"
19
+ __version__ = "0.72.45"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -20,7 +20,7 @@ from ._gateway_client import (
20
20
  resolve_self_name,
21
21
  resolve_token,
22
22
  )
23
- from swarph_cli.redact import redact_text
23
+ from swarph_cli.redact import encode_outbound_json, redact_text
24
24
 
25
25
 
26
26
  def _build_parser() -> argparse.ArgumentParser:
@@ -47,6 +47,34 @@ def _build_parser() -> argparse.ArgumentParser:
47
47
  return p
48
48
 
49
49
 
50
+ class ChainTokenAttachError(RuntimeError):
51
+ """The redacted event bytes are not JSON, so the chain token is not sent."""
52
+
53
+
54
+ def _attach_chain_token(data: bytes, chain_token: str) -> bytes:
55
+ """Add the CLI chain token onto an already-redacted event body.
56
+
57
+ Same shape as ``mcp_server._search``: redact first, then set the one
58
+ field that must reach the wire. A pasted chain_token line in the payload
59
+ was already scrubbed and is not this value.
60
+
61
+ Redaction can rewrite a quote inside the content string and leave bytes
62
+ that are not JSON. That refusal is by name (``JSONDecodeError`` or
63
+ ``UnicodeDecodeError``). The token is not copied into the error, and the
64
+ caller sends nothing.
65
+ """
66
+ try:
67
+ body_obj = json.loads(data.decode("utf-8"))
68
+ inner = json.loads(body_obj["content"])
69
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
70
+ raise ChainTokenAttachError(
71
+ f"_attach_chain_token refused: {type(exc).__name__}"
72
+ ) from None
73
+ inner["swarph_event"]["chain_token"] = chain_token
74
+ body_obj["content"] = json.dumps(inner, separators=(",", ":"), sort_keys=True)
75
+ return json.dumps(body_obj).encode("utf-8")
76
+
77
+
50
78
  def _post_channel(
51
79
  *,
52
80
  channel: str,
@@ -76,8 +104,6 @@ def _post_channel(
76
104
  base = (gateway or env_gateway()).rstrip("/")
77
105
  payload = redact_text(payload, caller="event.emit") or ""
78
106
  ev: dict = {"event": event, "payload": payload}
79
- if chain_token is not None: # carry an explicit "" through; only omit when unset
80
- ev["chain_token"] = chain_token
81
107
  # Sentinel-wrapped so P1's guard can UNAMBIGUOUSLY identify an event post: a
82
108
  # normal channel message won't carry the top-level `swarph_event` key, so the
83
109
  # guard keys off `content.swarph_event` — never a false-positive on a plain
@@ -91,7 +117,28 @@ def _post_channel(
91
117
  "kind": "fyi",
92
118
  "content": json.dumps(content, separators=(",", ":"), sort_keys=True),
93
119
  }
94
- status, resp = post_json(f"{base}/messages", body, token)
120
+ # Redact with no chain token in the body. Attach the CLI value after, so
121
+ # only that value skips redaction. An explicit "" is carried; omit only
122
+ # when the flag was unset. Card #1472.
123
+ if chain_token is None:
124
+ status, resp = post_json(f"{base}/messages", body, token)
125
+ else:
126
+ # Attach after redaction. If those bytes are not JSON, name the
127
+ # refusal and return before post_json: nothing is sent, no traceback.
128
+ try:
129
+ encoded = _attach_chain_token(
130
+ encode_outbound_json(body, caller="event.emit"),
131
+ chain_token,
132
+ )
133
+ except ChainTokenAttachError as exc:
134
+ print(f"swarph event: {exc}", file=sys.stderr)
135
+ return 2
136
+ status, resp = post_json(
137
+ f"{base}/messages",
138
+ body,
139
+ token,
140
+ encoded=encoded,
141
+ )
95
142
  if 200 <= status < 300:
96
143
  print(f"emitted event={event} channel={channel} id={resp.get('id')}")
97
144
  return 0
@@ -89,10 +89,16 @@ def _search(query: str, *, url: str, token: str | None = None) -> list[dict]:
89
89
  transport exception.
90
90
  """
91
91
  endpoint = url.rstrip("/") + "/api/ask"
92
- payload: dict = {"message": query}
92
+ # Redact the query only. The auth token is the credential this call
93
+ # presents to the search face. Putting it in the dict that
94
+ # encode_outbound_json walks replaces a credential-shaped value
95
+ # (secrets.token_urlsafe(32), the gateway mint) with
96
+ # [REDACTED:HIGH_ENTROPY] before the request is sent. Card #1472.
97
+ data = encode_outbound_json({"message": query}, caller="mcp_server._search")
93
98
  if token:
94
- payload["token"] = token
95
- data = encode_outbound_json(payload, caller="mcp_server._search")
99
+ body_obj = json.loads(data.decode("utf-8"))
100
+ body_obj["token"] = token
101
+ data = json.dumps(body_obj).encode("utf-8")
96
102
  req = urllib.request.Request(
97
103
  endpoint,
98
104
  data=data,
@@ -444,9 +444,16 @@ def _post_json(
444
444
  token: str,
445
445
  *,
446
446
  timeout: float = 10.0,
447
+ encoded: bytes | None = None,
447
448
  ) -> tuple[int, dict]:
448
449
  _require_absolute_gateway_url(url)
449
- data = encode_outbound_json(body, caller="mesh._post_json")
450
+ # event.emit redacts with no chain token, then passes the bytes with the
451
+ # CLI --chain-token already attached. Encoding those bytes again would
452
+ # strip that one value. Every other caller leaves encoded unset.
453
+ if encoded is None:
454
+ data = encode_outbound_json(body, caller="mesh._post_json")
455
+ else:
456
+ data = encoded
450
457
  req = urllib.request.Request(
451
458
  url,
452
459
  data=data,
@@ -132,6 +132,11 @@ _ANTHROPIC_RE = re.compile(r"\bsk-ant-[A-Za-z0-9\-_]{8,}\b")
132
132
  _OPENAI_RE = re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b")
133
133
  _XAI_RE = re.compile(r"\bxai-[A-Za-z0-9\-_]{16,}\b")
134
134
  _GCP_RE = re.compile(r"\bAIza[0-9A-Za-z\-_]{35}\b")
135
+ _GCP_FINAL_RE = re.compile(
136
+ r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
137
+ r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
138
+ r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"
139
+ )
135
140
  _SLACK_RE = re.compile(r"\bxox[baprs]-[A-Za-z0-9\-]{8,}\b")
136
141
  _AWS_KEY_RE = re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b")
137
142
  _AWS_SECRET_RE = re.compile(
@@ -332,6 +337,7 @@ def redact_text(
332
337
  # Layer 6: Shannon Entropy Scoring
333
338
  out = _SECRET_ASSIGN_RE.sub(_replace_secret_assignment, out)
334
339
  out = _replace_standalone_tokens(out)
340
+ out = _GCP_FINAL_RE.sub("[REDACTED:GCP_API_KEY]", out)
335
341
 
336
342
  return out
337
343
 
@@ -39,6 +39,13 @@ _SECRET_PATTERNS = [
39
39
  # password/token/secret assignments.
40
40
  (re.compile(r"(?i)\b(password|passwd|pwd|api[_-]?key|auth[_-]?token|secret)\b\s*[:=]\s*\S+"),
41
41
  r"\1=[REDACTED]"),
42
+ (re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b"),
43
+ "[REDACTED-API-KEY]"),
44
+ (re.compile(
45
+ r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
46
+ r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
47
+ r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"),
48
+ "[REDACTED-API-KEY]"),
42
49
  ]
43
50
 
44
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.44
3
+ Version: 0.72.45
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
@@ -285,6 +285,7 @@ tests/test_1452_rework2_engine.py
285
285
  tests/test_1452_rework3_engine.py
286
286
  tests/test_1452_standing_wake_expiry.py
287
287
  tests/test_1470_gh_router_fail_closed.py
288
+ tests/test_1472_redact_boundaries.py
288
289
  tests/test_1478_triage_dm.py
289
290
  tests/test_183_card_anchor.py
290
291
  tests/test_184_notify_honesty.py
@@ -0,0 +1,218 @@
1
+ """Card #1472: pasted chain_token text stays scrubbed, and a trailing-dash
2
+ AIza key is scrubbed too.
3
+
4
+ Every secret is built at runtime. No realistic key literal.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+
10
+ from swarph_cli.redact import encode_outbound_json, redact_text
11
+ from swarph_cli.scrub import scrub_text
12
+
13
+
14
+ def _sk_proj() -> str:
15
+ return "sk-proj-" + ("n7" * 12)
16
+
17
+
18
+ def _ghp() -> str:
19
+ return "ghp_" + ("Ab" * 16)
20
+
21
+
22
+ def _aiza_trailing_dash() -> str:
23
+ # AIza + 35 chars, last character '-'. No digits, so the entropy
24
+ # layer does not also eat it: only the AIza rule can.
25
+ secret = "AI" + "za" + ("m" * 34) + "-"
26
+ assert len(secret) == 39
27
+ assert secret.endswith("-")
28
+ return secret
29
+
30
+
31
+ def test_dm_body_pasted_chain_token_line_is_fully_redacted():
32
+ """Drop's measured leak: keys sit inside a pasted "chain_token": "..." value.
33
+
34
+ encode_outbound_json is what mesh._post_json sends. Both keys must be
35
+ gone from those bytes. A shield that puts the quoted span back fails this.
36
+ """
37
+ sk = _sk_proj()
38
+ ghp = _ghp()
39
+ content = (
40
+ 'pasted log "chain_token": "\n'
41
+ f"export OPENAI_API_KEY={sk}\n"
42
+ f"GITHUB={ghp}\n"
43
+ '" end'
44
+ )
45
+ followed = f"chain_token: note\n{sk}\n{ghp}"
46
+ for text in (content, followed):
47
+ body = {
48
+ "from_node": "friendly-coder",
49
+ "to_node": "lab-ovh",
50
+ "kind": "answer",
51
+ "content": text,
52
+ }
53
+ raw = encode_outbound_json(body, caller="t").decode("utf-8")
54
+ assert sk not in raw
55
+ assert ghp not in raw
56
+ assert "REDACTED" in raw
57
+
58
+
59
+ def test_redact_text_redacts_chain_token_json_holding_an_sk_proj_key():
60
+ sk = _sk_proj()
61
+ blob = '{"chain_token": "' + sk + '"}'
62
+ out = redact_text(blob, caller="t")
63
+ assert sk not in out
64
+ assert "REDACTED" in out
65
+
66
+
67
+ def test_chain_placeholder_literal_is_left_alone():
68
+ literal = "SWARPH.CHAIN.0.END"
69
+ assert redact_text(f"see {literal} here", caller="t") == f"see {literal} here"
70
+ sk = _sk_proj()
71
+ text = literal + '\n{"chain_token": "' + sk + '"}'
72
+ out = redact_text(text, caller="t")
73
+ assert out.startswith(literal)
74
+ assert sk not in out
75
+ assert json.loads(out.split("\n", 1)[1])["chain_token"] != sk
76
+
77
+
78
+ def test_aiza_key_then_hyphen_suffix_is_redacted():
79
+ """A '-' after the 35-character body is a boundary, not a longer tail.
80
+
81
+ key-x and key-- must be scrubbed. Putting '-' back into the lookahead
82
+ (?![0-9A-Za-z\\-_]) hides both, because the next character is '-'.
83
+ """
84
+ secret = "AI" + "za" + ("m" * 35)
85
+ assert len(secret) == 39
86
+ assert not secret.endswith("-")
87
+ for suffix in ("-x", "--"):
88
+ text = secret + suffix
89
+ scrubbed = scrub_text(text)
90
+ assert scrubbed is not None
91
+ assert secret not in scrubbed
92
+ assert "REDACTED" in scrubbed
93
+ redacted = redact_text(text, caller="t")
94
+ assert secret not in redacted
95
+ assert "REDACTED" in redacted
96
+
97
+
98
+ def _assert_scrub_and_redact(text: str, *secrets: str) -> None:
99
+ scrubbed = scrub_text(text)
100
+ assert scrubbed is not None
101
+ redacted = redact_text(text, caller="t")
102
+ for secret in secrets:
103
+ assert secret not in scrubbed
104
+ assert secret not in redacted
105
+ assert "REDACTED" in scrubbed
106
+ assert "REDACTED" in redacted
107
+
108
+
109
+ def test_aiza_dash_key_followed_by_word_char_is_redacted():
110
+ """A 35th character of '-' then x, _, or 'abc def' (drop #4429).
111
+
112
+ {35}(?![0-9A-Za-z_]) refuses that follower, so the key stays visible.
113
+ Both scrub_text and redact_text have to take the dash.
114
+ """
115
+ secret = _aiza_trailing_dash()
116
+ for suffix in ("x", "_", "abc def"):
117
+ _assert_scrub_and_redact(secret + suffix, secret)
118
+
119
+
120
+ def test_dash_ending_aiza_then_api_key_hides_key_and_secret():
121
+ """:<dash-ending key>api_key=<secret> leaves neither (drop #4437).
122
+
123
+ The match stops on the key. A greedy tail would swallow api_key, and
124
+ the secret would stay where the keyword-value rule should have put
125
+ [REDACTED:DSN_CREDENTIAL].
126
+ """
127
+ key = _aiza_trailing_dash()
128
+ secret = "plain-" + "secret-value"
129
+ text = f":{key}api_key={secret}"
130
+ scrubbed = scrub_text(text)
131
+ assert scrubbed is not None
132
+ assert key not in scrubbed
133
+ assert secret not in scrubbed
134
+ assert "api_key=[REDACTED]" in scrubbed
135
+ redacted = redact_text(text, caller="t")
136
+ assert key not in redacted
137
+ assert secret not in redacted
138
+ assert "api_key=[REDACTED:DSN_CREDENTIAL]" in redacted
139
+
140
+
141
+ def test_dash_ending_aiza_glued_through_chain_token_redacts_both_keys():
142
+ """token=<dash-ending key>--chain_token<full key> leaves neither key.
143
+
144
+ A match that stops on the first key leaves the second glued to the
145
+ word character at the end of chain_token. Values are built here, with
146
+ no digits, so the entropy layer cannot hide a miss.
147
+ """
148
+ dash_key = _aiza_trailing_dash()
149
+ full_key = "AI" + "za" + ("n" * 35)
150
+ assert len(full_key) == 39
151
+ assert "-" not in full_key
152
+ text = f"token={dash_key}--chain_token{full_key}"
153
+ _assert_scrub_and_redact(text, dash_key, full_key)
154
+
155
+
156
+ def test_drop_4452_chain_and_proj_shapes_leave_no_key():
157
+ """Drop #4452's two shapes, on scrub_text and redact_text.
158
+
159
+ chain_token<dash key>x<full key>{ leaves neither key. A proj key glued
160
+ to a dash-ending key and then api_key=<another dash key> leaves none of
161
+ the three. The keyword value below is a low-entropy secret: scrub_text
162
+ must hide it. redact_text's OpenAI rule is main's, and it still runs
163
+ before the keyword rule, so that low-entropy value can remain there.
164
+ """
165
+ dash = _aiza_trailing_dash()
166
+ full = "AI" + "za" + ("n" * 35)
167
+ _assert_scrub_and_redact(f"chain_token{dash}x{full}{{", dash, full)
168
+
169
+ proj = "sk-proj-" + ("n7" * 12)
170
+ trail = "AI" + "za" + ("p" * 34) + "-"
171
+ assert len(trail) == 39 and trail.endswith("-")
172
+ _assert_scrub_and_redact(f"{proj}{dash}api_key={trail}", proj, dash, trail)
173
+
174
+ secret = "plain-" + "secret-value"
175
+ text = f"{proj}{dash}api_key={secret}"
176
+ scrubbed = scrub_text(text)
177
+ assert scrubbed is not None
178
+ assert proj not in scrubbed
179
+ assert dash not in scrubbed
180
+ assert secret not in scrubbed
181
+ redacted = redact_text(text, caller="t")
182
+ assert proj not in redacted
183
+ assert dash not in redacted
184
+
185
+
186
+ def test_dash_ending_key_glued_after_word_char_is_redacted():
187
+ """A dash-ending key glued after x, _, another key, or é is redacted.
188
+
189
+ No digits, so the entropy layer cannot hide a miss. Both scrub_text
190
+ and redact_text have to take the key.
191
+ """
192
+ dash = _aiza_trailing_dash()
193
+ other = "AI" + "za" + ("q" * 35)
194
+ assert len(other) == 39
195
+ for prefix in ("x", "_", other, "é"):
196
+ _assert_scrub_and_redact(prefix + dash, dash)
197
+
198
+
199
+ def test_aiza_key_ending_in_dash_is_redacted_before_space_quote_or_punctuation():
200
+ secret = _aiza_trailing_dash()
201
+ samples = [
202
+ f"prefix {secret} suffix",
203
+ f'"{secret}"',
204
+ f"'{secret}'",
205
+ secret,
206
+ f"{secret}.",
207
+ f"{secret},",
208
+ f"({secret})",
209
+ f"{secret}\nnext",
210
+ ]
211
+ for text in samples:
212
+ scrubbed = scrub_text(text)
213
+ assert scrubbed is not None
214
+ assert secret not in scrubbed
215
+ assert "REDACTED" in scrubbed
216
+ redacted = redact_text(text, caller="t")
217
+ assert secret not in redacted
218
+ assert "REDACTED" in redacted
@@ -0,0 +1,166 @@
1
+ def test_event_emit_posts_with_tag_and_token(monkeypatch):
2
+ from swarph_cli.commands import event as evmod
3
+ captured = {}
4
+ monkeypatch.setattr(evmod, "_post_channel", lambda **kw: captured.update(kw) or 0)
5
+ rc = evmod.run_event(["emit", "stage-done", "the output", "--chain-token", "TOK", "--channel", "events"])
6
+ assert rc == 0
7
+ assert captured["channel"] == "events"
8
+ assert captured["event"] == "stage-done"
9
+ assert captured["payload"] == "the output"
10
+ assert captured["chain_token"] == "TOK"
11
+
12
+ def test_event_emit_no_token(monkeypatch):
13
+ from swarph_cli.commands import event as evmod
14
+ captured = {}
15
+ monkeypatch.setattr(evmod, "_post_channel", lambda **kw: captured.update(kw) or 0)
16
+ evmod.run_event(["emit", "e", "p"])
17
+ assert captured.get("chain_token") is None # argparse default=None, not ""
18
+
19
+
20
+ def test_event_emit_real_envelope_is_sentinel_wrapped(monkeypatch):
21
+ # exercise the REAL _post_channel (not the seam) to prove the content envelope is
22
+ # sentinel-wrapped so P1's guard identifies events unambiguously (a plain channel
23
+ # message won't carry the top-level `swarph_event` key). The CLI chain token is
24
+ # attached on the bytes after redaction, not inside the pre-redact body.
25
+ import json
26
+ from swarph_cli.commands import event as evmod
27
+ monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "lab-ovh")
28
+ monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
29
+ captured = {}
30
+
31
+ def _capture(url, body, token, encoded=None, timeout=10.0):
32
+ captured.update(body=body, encoded=encoded)
33
+ return 200, {"id": 1}
34
+
35
+ monkeypatch.setattr(evmod, "post_json", _capture)
36
+ rc = evmod.run_event(["emit", "stage-done", "out", "--chain-token", "TOK"])
37
+ assert rc == 0
38
+ content = json.loads(captured["body"]["content"])
39
+ assert "swarph_event" in content
40
+ assert content["swarph_event"] == {"event": "stage-done", "payload": "out"}
41
+ assert captured["body"]["kind"] == "fyi" # VALID_KINDS-safe; P1 promotes to first-class
42
+ wire = json.loads(json.loads(captured["encoded"])["content"])
43
+ assert wire["swarph_event"] == {"event": "stage-done", "payload": "out", "chain_token": "TOK"}
44
+
45
+
46
+ def test_chain_token_reaches_the_wire_and_the_payload_is_still_scrubbed(monkeypatch):
47
+ """The CLI --chain-token is attached after redaction, like _search.
48
+
49
+ A producer-signed token and a dotless credential-shaped token both reach
50
+ the wire. A classic key in the payload does not. A pasted chain_token
51
+ field is not this bypass.
52
+ """
53
+ import json
54
+ import secrets
55
+
56
+ from swarph_cli.chain_token import sign_chain_token
57
+ from swarph_cli.commands import event as evmod
58
+ from swarph_cli.redact import redact_text
59
+
60
+ signed = sign_chain_token("producer-secret-xyz", "chain-1", 1, "jti-abc")
61
+ opaque = None
62
+ for _ in range(40):
63
+ candidate = secrets.token_urlsafe(32)
64
+ if redact_text(candidate, caller="t") != candidate:
65
+ opaque = candidate
66
+ break
67
+ assert opaque is not None
68
+ classic = "sk-" + ("cd" * 12)
69
+
70
+ class _Resp:
71
+ status = 200
72
+
73
+ def __enter__(self):
74
+ return self
75
+
76
+ def __exit__(self, *args):
77
+ return False
78
+
79
+ def read(self):
80
+ return b'{"id": 7}'
81
+
82
+ def emit(chain):
83
+ captured = {}
84
+
85
+ def fake_urlopen(req, timeout=None):
86
+ captured["raw"] = req.data
87
+ return _Resp()
88
+
89
+ monkeypatch.setattr(
90
+ "swarph_cli.commands.mesh.urllib.request.urlopen", fake_urlopen
91
+ )
92
+ monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "friendly-coder")
93
+ monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
94
+ rc = evmod.run_event([
95
+ "emit", "stage-done", "note " + classic,
96
+ "--chain-token", chain,
97
+ "--gateway", "http://127.0.0.1:9",
98
+ ])
99
+ assert rc == 0, rc
100
+ body = json.loads(captured["raw"].decode("utf-8"))
101
+ return json.loads(body["content"])["swarph_event"]
102
+
103
+ for chain in (signed, opaque):
104
+ ev = emit(chain)
105
+ assert ev["chain_token"] == chain
106
+ assert classic not in ev["payload"]
107
+ assert "REDACTED" in ev["payload"]
108
+
109
+
110
+ def test_chain_token_on_unparseable_body_refuses_and_sends_nothing(monkeypatch, capsys):
111
+ """Redaction can break the content JSON (password="}, PASSWORD=", api_key=").
112
+
113
+ --chain-token must refuse by name, send nothing, and exit nonzero.
114
+ No traceback, and neither the payload nor the token reaches the stubbed wire.
115
+ """
116
+ from swarph_cli.commands import event as evmod
117
+
118
+ token = "chain-token-must-not-leak"
119
+ payloads = (
120
+ 'password="}',
121
+ 'export PASSWORD=" x',
122
+ 'api_key="',
123
+ )
124
+ calls = []
125
+
126
+ class _Resp:
127
+ status = 200
128
+
129
+ def __enter__(self):
130
+ return self
131
+
132
+ def __exit__(self, *args):
133
+ return False
134
+
135
+ def read(self):
136
+ return b'{"id": 1}'
137
+
138
+ def fake_urlopen(req, timeout=None):
139
+ calls.append(req.data or b"")
140
+ return _Resp()
141
+
142
+ monkeypatch.setattr(
143
+ "swarph_cli.commands.mesh.urllib.request.urlopen", fake_urlopen
144
+ )
145
+ monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "friendly-coder")
146
+ monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
147
+
148
+ for payload in payloads:
149
+ before = len(calls)
150
+ rc = evmod.run_event([
151
+ "emit", "stage-done", payload,
152
+ "--chain-token", token,
153
+ "--gateway", "http://127.0.0.1:9",
154
+ ])
155
+ captured = capsys.readouterr()
156
+ assert rc != 0
157
+ assert "Traceback" not in captured.err
158
+ assert "Traceback" not in captured.out
159
+ assert "_attach_chain_token refused: JSONDecodeError" in captured.err
160
+ assert token not in captured.err
161
+ assert token not in captured.out
162
+ assert payload not in captured.err
163
+ assert payload not in captured.out
164
+ assert calls[before:] == []
165
+
166
+ assert calls == []
@@ -94,6 +94,45 @@ def test_search_passes_token_when_given():
94
94
  assert captured["data"]["token"] == "secret"
95
95
 
96
96
 
97
+ def test_search_keeps_a_credential_shaped_token_and_scrubs_the_message():
98
+ """PR 648: the auth token used to ride inside the redacted body.
99
+
100
+ A gateway-mint shape (token_urlsafe(32)) is often replaced with
101
+ [REDACTED:HIGH_ENTROPY]. The message is still scrubbed.
102
+ """
103
+ import secrets
104
+
105
+ from swarph_cli.redact import redact_text
106
+
107
+ tok = None
108
+ for _ in range(40):
109
+ candidate = secrets.token_urlsafe(32)
110
+ if redact_text(candidate, caller="t") != candidate:
111
+ tok = candidate
112
+ break
113
+ assert tok is not None, "need one credential-shaped token the redactor would eat"
114
+ classic = "sk-" + ("ab" * 12)
115
+ captured = {}
116
+
117
+ def _fake(req, timeout=None): # noqa: ANN001 - test double
118
+ captured["data"] = json.loads(req.data.decode("utf-8"))
119
+ resp = mock.MagicMock()
120
+ resp.read.return_value = json.dumps({"results": []}).encode("utf-8")
121
+ resp.__enter__ = lambda s: s
122
+ resp.__exit__ = lambda s, *a: False
123
+ return resp
124
+
125
+ with mock.patch(
126
+ "swarph_cli.commands.mcp_server.urllib.request.urlopen", _fake
127
+ ):
128
+ mcp_server._search(
129
+ "look " + classic, url="https://metaedge.surf/", token=tok
130
+ )
131
+ assert captured["data"]["token"] == tok
132
+ assert classic not in captured["data"]["message"]
133
+ assert "REDACTED" in captured["data"]["message"]
134
+
135
+
97
136
  # --------------------------------------------------------------------------- #
98
137
  # _add — parse_uri gate + add.run_add dispatch
99
138
  # --------------------------------------------------------------------------- #
@@ -14,9 +14,15 @@ from swarph_cli.scrub import scrub_text
14
14
  @pytest.mark.parametrize("secret", [
15
15
  "sk-ant-aaaa-bbbb-cccc-dddd",
16
16
  "sk-abcdefghijklmnop123456",
17
+ # Hyphenated OpenAI shapes, built here so the source has no key literal.
18
+ # The old [A-Za-z0-9] class stopped at the hyphen (card #1472).
19
+ "sk-proj-" + ("n7" * 12),
20
+ "sk-svcacct-" + ("m8" * 12),
17
21
  "xai-abcdefghijklmnop123456",
18
22
  # Built at runtime: the literal full shape trips gitleaks gcp-api-key.
19
23
  "AIza" + "SyDdI0T9mbB4y7KzQ3wX2vU5tS8rR1pP6oO",
24
+ # 35th character is '-'. A closing \b does not match before the space.
25
+ "AI" + "za" + ("m" * 34) + "-",
20
26
  "ghp_abcdefghijklmnopqrstuvwx1234567890",
21
27
  # Built at runtime: the literal full shape trips push protection.
22
28
  "xoxb-" + "123456789012-abcdefghijklmnop",