swarph-cli 0.72.35__tar.gz → 0.72.37__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 (560) hide show
  1. {swarph_cli-0.72.35/src/swarph_cli.egg-info → swarph_cli-0.72.37}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/backends.py +5 -3
  5. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/board.py +106 -37
  6. swarph_cli-0.72.37/src/swarph_cli/commands/board_cost.py +199 -0
  7. swarph_cli-0.72.37/src/swarph_cli/commands/board_usage.py +378 -0
  8. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/brain_ask.py +2 -1
  9. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/cell_probe.py +3 -1
  10. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/channel.py +8 -4
  11. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/codegraph_hook.py +2 -1
  12. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/commander.py +82 -5
  13. swarph_cli-0.72.37/src/swarph_cli/commands/committee.py +247 -0
  14. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/event.py +2 -0
  15. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/gh_route.py +21 -14
  16. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/group.py +2 -1
  17. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/highlight.py +19 -6
  18. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/hooks.py +42 -6
  19. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/mcp_server.py +3 -2
  20. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/memory_emit_hook.py +6 -2
  21. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/memory_sync.py +17 -3
  22. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/mesh.py +100 -20
  23. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/onboard.py +2 -1
  24. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/ratify.py +2 -2
  25. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/roster.py +5 -3
  26. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/sweep.py +197 -24
  27. swarph_cli-0.72.37/src/swarph_cli/commit_trailers.py +31 -0
  28. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dm_frame.py +19 -1
  29. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/slm.py +4 -2
  30. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/duplicate_session.py +144 -16
  31. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/server.py +3 -1
  32. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gh_identity.py +343 -2
  33. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/guide/GUIDE.md +3 -0
  34. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/main.py +1 -0
  35. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/api.py +7 -1
  36. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_service_reply.py +2 -0
  37. swarph_cli-0.72.37/src/swarph_cli/redact.py +417 -0
  38. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/dm_notify_filter.py +5 -2
  39. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/stall_alert.py +31 -24
  40. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/backend.py +6 -0
  41. {swarph_cli-0.72.35 → swarph_cli-0.72.37/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  42. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli.egg-info/SOURCES.txt +15 -0
  43. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1006_session_board.py +30 -0
  44. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1064_stuck_sweep.py +224 -9
  45. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1068_structured_accept_cli.py +6 -2
  46. swarph_cli-0.72.37/tests/test_1277_close_usage.py +307 -0
  47. swarph_cli-0.72.37/tests/test_1300_authorization_check.py +83 -0
  48. swarph_cli-0.72.37/tests/test_1320_commit_trailers.py +56 -0
  49. swarph_cli-0.72.37/tests/test_1321_layered_redaction.py +1072 -0
  50. swarph_cli-0.72.37/tests/test_1340_router_rollout.py +79 -0
  51. swarph_cli-0.72.37/tests/test_1346_shell_string_gh.py +125 -0
  52. swarph_cli-0.72.37/tests/test_1347_shell_depth_cap.py +105 -0
  53. swarph_cli-0.72.37/tests/test_3130_sender.py +28 -0
  54. swarph_cli-0.72.37/tests/test_3150_identity_router_install.py +44 -0
  55. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_397_router_preflight.py +12 -12
  56. swarph_cli-0.72.37/tests/test_513_duplicate_session.py +489 -0
  57. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_591_cli_step_graph.py +3 -1
  58. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_board_obligation_close.py +8 -2
  59. swarph_cli-0.72.37/tests/test_committee_command.py +160 -0
  60. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_init.py +12 -2
  61. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_stall_alert.py +61 -32
  62. swarph_cli-0.72.35/tests/test_513_duplicate_session.py +0 -215
  63. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/LICENSE +0 -0
  64. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/README.md +0 -0
  65. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/setup.cfg +0 -0
  66. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/__main__.py +0 -0
  67. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/__init__.py +0 -0
  68. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/claude_cli.py +0 -0
  69. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/codex_cli.py +0 -0
  70. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/pack.py +0 -0
  71. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/prices.py +0 -0
  72. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/providers.py +0 -0
  73. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/quality.py +0 -0
  74. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/refresh_prices.py +0 -0
  75. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/rules.py +0 -0
  76. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/runner.py +0 -0
  77. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/signal_score.py +0 -0
  78. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/bench/validate.py +0 -0
  79. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/caller.py +0 -0
  80. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/__init__.py +0 -0
  81. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/harden.py +0 -0
  82. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/lineage.py +0 -0
  83. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/liveness.py +0 -0
  84. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/manifest.py +0 -0
  85. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/paths.py +0 -0
  86. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/capture/verify.py +0 -0
  87. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/cell.py +0 -0
  88. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/chain_token.py +0 -0
  89. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/channel.py +0 -0
  90. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/__init__.py +0 -0
  91. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/_content.py +0 -0
  92. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/_display.py +0 -0
  93. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/_gateway_client.py +0 -0
  94. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/add.py +0 -0
  95. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/bench.py +0 -0
  96. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/board_merge_check.py +0 -0
  97. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/brain.py +0 -0
  98. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/cell.py +0 -0
  99. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  100. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/chat.py +0 -0
  101. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/codegraph.py +0 -0
  102. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  103. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/codex_waker.py +0 -0
  104. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/compress.py +0 -0
  105. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/daemon.py +0 -0
  106. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/dreaming.py +0 -0
  107. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/gateway.py +0 -0
  108. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/guide.py +0 -0
  109. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  110. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/hook_output.py +0 -0
  111. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/import_session.py +0 -0
  112. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/init.py +0 -0
  113. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  114. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_hook.py +0 -0
  115. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  116. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  117. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  118. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  119. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/lane.py +0 -0
  120. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/memory.py +0 -0
  121. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/monitor.py +0 -0
  122. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/okf_links.py +0 -0
  123. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  124. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  125. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/protocol_handler.py +0 -0
  126. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  127. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/schedule.py +0 -0
  128. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/security.py +0 -0
  129. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/service.py +0 -0
  130. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/spawn.py +0 -0
  131. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/timeline.py +0 -0
  132. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/triage.py +0 -0
  133. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/version.py +0 -0
  134. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  135. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/commands/watchdog.py +0 -0
  136. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/compress/__init__.py +0 -0
  137. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/compress/levers.py +0 -0
  138. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/compress/marker.py +0 -0
  139. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/compress/verify.py +0 -0
  140. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/console_safe.py +0 -0
  141. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/delivery_queue.py +0 -0
  142. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/__init__.py +0 -0
  143. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/candidates.py +0 -0
  144. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/clone.py +0 -0
  145. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/enrich.py +0 -0
  146. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/index_check.py +0 -0
  147. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/notify.py +0 -0
  148. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/organize.py +0 -0
  149. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/probes.py +0 -0
  150. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/report.py +0 -0
  151. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/run.py +0 -0
  152. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/transcripts.py +0 -0
  153. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/dreaming/verify.py +0 -0
  154. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/__init__.py +0 -0
  155. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/feature_registry.py +0 -0
  156. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/lanes_control.py +0 -0
  157. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/schema.sql +0 -0
  158. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway/services_control.py +0 -0
  159. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/gateway_default.py +0 -0
  160. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/identity.py +0 -0
  161. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  162. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  163. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  164. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  165. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  166. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/launchd_render.py +0 -0
  167. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/multiplexer.py +0 -0
  168. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/__init__.py +0 -0
  169. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/__main__.py +0 -0
  170. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/cli.py +0 -0
  171. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/config.py +0 -0
  172. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/operator/fmt.py +0 -0
  173. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/orphan_daemons.py +0 -0
  174. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/pane_probe.py +0 -0
  175. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/parsers/__init__.py +0 -0
  176. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/parsers/claude.py +0 -0
  177. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  178. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  179. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  180. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  181. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  182. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  183. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  184. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  185. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  186. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  187. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  188. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  189. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_executor.py +0 -0
  190. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_executor_windows.py +0 -0
  191. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_reconciliation.py +0 -0
  192. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_reply_outbox.py +0 -0
  193. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_service_host.py +0 -0
  194. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_service_payload.py +0 -0
  195. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/peer_staging.py +0 -0
  196. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/query_filters.py +0 -0
  197. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/quota_sync.py +0 -0
  198. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/rowclear.py +0 -0
  199. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/__init__.py +0 -0
  200. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  201. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  202. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  203. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  204. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/scrub.py +0 -0
  205. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/service/__init__.py +0 -0
  206. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/service/app.py +0 -0
  207. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/service/providers.py +0 -0
  208. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/session_bridge.py +0 -0
  209. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  210. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  211. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/source_text.py +0 -0
  212. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/codex-waker.default +0 -0
  213. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  214. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  215. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  216. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  217. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  218. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  219. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  220. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  221. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  222. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  223. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  224. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  225. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  226. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  227. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  228. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  229. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  230. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/the_ten.py +0 -0
  231. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/timeline_paths.py +0 -0
  232. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/tokens.py +0 -0
  233. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/__init__.py +0 -0
  234. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/job.py +0 -0
  235. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/owners.json +0 -0
  236. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/router.py +0 -0
  237. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/rows.py +0 -0
  238. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli/triage/sensor.py +0 -0
  239. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  240. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  241. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli.egg-info/requires.txt +0 -0
  242. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/src/swarph_cli.egg-info/top_level.txt +0 -0
  243. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1006_board_publisher.py +0 -0
  244. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1010_strand_only.py +0 -0
  245. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1013_spawn_channel.py +0 -0
  246. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1017_muse_tui.py +0 -0
  247. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1034_copy_mode.py +0 -0
  248. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1050_surrogate_wake.py +0 -0
  249. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1052_opencode_deaf.py +0 -0
  250. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1059_watchdog_unread_env.py +0 -0
  251. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1061_roster_cli.py +0 -0
  252. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1065_release.py +0 -0
  253. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1066_quiet_wakes.py +0 -0
  254. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1071_quota_sync.py +0 -0
  255. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1073_triage.py +0 -0
  256. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1073_triage_board.py +0 -0
  257. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1073_wheel_install.py +0 -0
  258. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1075_task_cli.py +0 -0
  259. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1083_openai_decisions.py +0 -0
  260. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1144_cell_yaml_github.py +0 -0
  261. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1197_commander_taps.py +0 -0
  262. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1203_swarph_me_refusal.py +0 -0
  263. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1206_offer_question.py +0 -0
  264. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1214_egress.py +0 -0
  265. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1219_egress_probe.py +0 -0
  266. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1221_quota_windows.py +0 -0
  267. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1222_maybe_start_cap.py +0 -0
  268. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1228_quota_reach.py +0 -0
  269. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1243_the_ten.py +0 -0
  270. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1248_ship_price_table.py +0 -0
  271. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_124_register_merge.py +0 -0
  272. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1250_price_cache.py +0 -0
  273. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1256_commander_wake.py +0 -0
  274. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1261_mcp_install.py +0 -0
  275. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1262_wake_followups.py +0 -0
  276. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1265_wake_note.py +0 -0
  277. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1269_triage_label.py +0 -0
  278. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_126_pending_from_complete.py +0 -0
  279. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1278_dreaming_reach.py +0 -0
  280. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1286_dm_unread_cursor.py +0 -0
  281. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1297_commander.py +0 -0
  282. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_1301_restart_skew.py +0 -0
  283. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  284. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_138_memory_navigate.py +0 -0
  285. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_139_tool_probe.py +0 -0
  286. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_183_card_anchor.py +0 -0
  287. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_184_notify_honesty.py +0 -0
  288. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_201_win32_bash_refusal.py +0 -0
  289. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_243_onboard_per_peer_token.py +0 -0
  290. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_307_task4_ask_reply.py +0 -0
  291. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  292. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_319_refusal_exit_codes.py +0 -0
  293. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_323_mistral_backend.py +0 -0
  294. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_332_report_organize_findings.py +0 -0
  295. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_356_query_filters.py +0 -0
  296. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_397_gh_identity_router.py +0 -0
  297. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_402_one_identity_resolver.py +0 -0
  298. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_432_rowclear.py +0 -0
  299. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_467b_onboard_operator_credential.py +0 -0
  300. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_472_register_caller_binding.py +0 -0
  301. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_484_codex_queue_sink.py +0 -0
  302. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_523_guide.py +0 -0
  303. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_524_conftest_hermeticity.py +0 -0
  304. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_532_accept_flag.py +0 -0
  305. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_535_version_capability.py +0 -0
  306. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_538_signal_bench.py +0 -0
  307. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_546_default_gateway.py +0 -0
  308. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_547_verbs_from_registry.py +0 -0
  309. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_548_gateway_remainder.py +0 -0
  310. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_552_codegraph_gateway.py +0 -0
  311. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_555_relay_row_shape.py +0 -0
  312. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_556_relay_freshness.py +0 -0
  313. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_559_edges_truncated.py +0 -0
  314. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_564_bootstrap_first_peer.py +0 -0
  315. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  316. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_579_register_bootstrap_message.py +0 -0
  317. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_644_monitor_supervision.py +0 -0
  318. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_648_install_unit.py +0 -0
  319. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_654_reexec.py +0 -0
  320. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_656_owned_slm.py +0 -0
  321. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_684_dreaming_verb.py +0 -0
  322. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_689_pkg_version_order.py +0 -0
  323. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_715_grok_wake.py +0 -0
  324. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_716_timeline_one_path.py +0 -0
  325. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_723_grok_0251.py +0 -0
  326. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  327. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_725_stdio_reconfigure.py +0 -0
  328. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_726_grok_limit_screen.py +0 -0
  329. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_729_arm_check_delivery_gap.py +0 -0
  330. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_729_dm_once.py +0 -0
  331. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_729_wake_watchdog.py +0 -0
  332. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  333. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_735_dm_mcp_tools.py +0 -0
  334. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_739_wake_writer.py +0 -0
  335. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_740_edit_project.py +0 -0
  336. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_762_cards_edit_priority.py +0 -0
  337. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_764_enrich_collapse.py +0 -0
  338. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_801_confirm_flow_cli.py +0 -0
  339. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_802_stage_history.py +0 -0
  340. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_825_codegraph_hook_noise.py +0 -0
  341. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_825_install_posttooluse_only.py +0 -0
  342. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  343. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_829_counterfactual_column.py +0 -0
  344. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_830_hook_binding_version.py +0 -0
  345. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_84_cli_labels.py +0 -0
  346. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_866_doctrine_guide.py +0 -0
  347. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_872_identity_refuses_not_warns.py +0 -0
  348. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_874_source_text.py +0 -0
  349. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  350. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_931_board_search.py +0 -0
  351. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_935_deadline_clock.py +0 -0
  352. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_937_dreaming_notify.py +0 -0
  353. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_938_organize_line_limit.py +0 -0
  354. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_941_bench_backends.py +0 -0
  355. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_941_harness_342.py +0 -0
  356. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_945_same_line_pairing.py +0 -0
  357. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_955_prompt_audit.py +0 -0
  358. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_957_auth_http.py +0 -0
  359. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_959_venv_interpreter.py +0 -0
  360. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_960_channel.py +0 -0
  361. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  362. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_961_cursor_grok_footer.py +0 -0
  363. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_961_loud_stop.py +0 -0
  364. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_963_plugin_manifest.py +0 -0
  365. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_964_channel_cell.py +0 -0
  366. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_965_mesh_wait.py +0 -0
  367. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_975_channel_hop.py +0 -0
  368. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_add_security_gate.py +0 -0
  369. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_add.py +0 -0
  370. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_hash.py +0 -0
  371. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_lib.py +0 -0
  372. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_mcp.py +0 -0
  373. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_skill.py +0 -0
  374. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_tool.py +0 -0
  375. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_artifact_uri.py +0 -0
  376. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_backends.py +0 -0
  377. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_claude_cli.py +0 -0
  378. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_codex_cli.py +0 -0
  379. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_command.py +0 -0
  380. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_pack.py +0 -0
  381. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_prices.py +0 -0
  382. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_quality.py +0 -0
  383. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_runner.py +0 -0
  384. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_bench_validate.py +0 -0
  385. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_board_card_thread.py +0 -0
  386. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_board_command.py +0 -0
  387. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_board_merge_check.py +0 -0
  388. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_brain_ask_command.py +0 -0
  389. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_brain_ask_env_diagnosis.py +0 -0
  390. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_brain_ask_identity_explicitness.py +0 -0
  391. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_brain_command.py +0 -0
  392. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_caller_meta_guard.py +0 -0
  393. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_capture_lineage.py +0 -0
  394. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_capture_liveness.py +0 -0
  395. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_capture_manifest.py +0 -0
  396. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_capture_paths.py +0 -0
  397. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_capture_security.py +0 -0
  398. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cell_command_dispatch.py +0 -0
  399. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cell_harden.py +0 -0
  400. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cell_loader.py +0 -0
  401. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cell_selfcheck.py +0 -0
  402. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cell_verify.py +0 -0
  403. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_chain_token.py +0 -0
  404. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_channel_command.py +0 -0
  405. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  406. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_channel_post.py +0 -0
  407. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_channel_wake_policy_filter.py +0 -0
  408. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_chat_command.py +0 -0
  409. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_claude_parser.py +0 -0
  410. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_claude_tmux_template.py +0 -0
  411. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_codegraph_caller_scope.py +0 -0
  412. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_codegraph_command.py +0 -0
  413. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_codegraph_hook.py +0 -0
  414. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_codegraph_mcp.py +0 -0
  415. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_codex_waker.py +0 -0
  416. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_compress_caller_convention.py +0 -0
  417. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_compress_command.py +0 -0
  418. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_compress_levers.py +0 -0
  419. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_compress_marker.py +0 -0
  420. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_compress_verify.py +0 -0
  421. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_console_encoding_e2e.py +0 -0
  422. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_content_composition.py +0 -0
  423. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_copilot_membrane.py +0 -0
  424. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_copilot_pane_reader.py +0 -0
  425. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_cursor_membrane.py +0 -0
  426. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_daemon_command.py +0 -0
  427. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_daemon_console_encoding.py +0 -0
  428. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_daemon_delivery.py +0 -0
  429. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_delivery_queue.py +0 -0
  430. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_display_sanitize.py +0 -0
  431. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  432. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_event_emit.py +0 -0
  433. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_feature_to_uri.py +0 -0
  434. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_gateway_brain.py +0 -0
  435. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_gateway_client.py +0 -0
  436. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_gateway_command.py +0 -0
  437. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_gateway_message_idempotency.py +0 -0
  438. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  439. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_group_command.py +0 -0
  440. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_guide_commands_parse.py +0 -0
  441. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_guide_selfcheck.py +0 -0
  442. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_heartbeat_check.py +0 -0
  443. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_highlight_command.py +0 -0
  444. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hook_output.py +0 -0
  445. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_activity_marker.py +0 -0
  446. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_add.py +0 -0
  447. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_bundle.py +0 -0
  448. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_legacy_generation_removal.py +0 -0
  449. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_lifecycle.py +0 -0
  450. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_merge.py +0 -0
  451. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_verify.py +0 -0
  452. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  453. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_hooks_win32_command_path.py +0 -0
  454. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_import_command.py +0 -0
  455. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_inbox_identity_provenance.py +0 -0
  456. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_init_command.py +0 -0
  457. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_init_provider_menu.py +0 -0
  458. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_install_codex_hooks.py +0 -0
  459. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_install_hook.py +0 -0
  460. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_install_multiplexer_command.py +0 -0
  461. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_install_postcompact_hook.py +0 -0
  462. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_install_wake_hook.py +0 -0
  463. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_lane_command.py +0 -0
  464. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_launchd_units.py +0 -0
  465. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_main.py +0 -0
  466. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_main_unknown_verb_guard.py +0 -0
  467. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mcp_missing_hint.py +0 -0
  468. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mcp_server.py +0 -0
  469. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_memory_command.py +0 -0
  470. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_memory_emit_snippet.py +0 -0
  471. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_memory_gateway.py +0 -0
  472. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_memory_sync.py +0 -0
  473. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_memory_sync_membrane.py +0 -0
  474. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_command.py +0 -0
  475. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_inbox_mark_read.py +0 -0
  476. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_inbox_sanitize.py +0 -0
  477. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_originate_thread_623.py +0 -0
  478. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_send_unknown_peer.py +0 -0
  479. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_sidecar.py +0 -0
  480. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  481. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  482. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  483. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_meta_edge_identity.py +0 -0
  484. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_monitor_channel_poll.py +0 -0
  485. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_monitor_cli.py +0 -0
  486. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_monitor_daemon_collision.py +0 -0
  487. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_monitor_self_name_verification.py +0 -0
  488. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_monitor_state_split.py +0 -0
  489. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_multiplexer.py +0 -0
  490. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_muse_membrane.py +0 -0
  491. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_muse_own_cli.py +0 -0
  492. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_obligation_close_is_surfaced.py +0 -0
  493. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_okf_links.py +0 -0
  494. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_onboard_command.py +0 -0
  495. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_opencode_membrane.py +0 -0
  496. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_opencode_plugin.py +0 -0
  497. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_operator_me.py +0 -0
  498. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_orphan_daemons.py +0 -0
  499. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_packaged_artifacts_exist.py +0 -0
  500. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_packaged_skills.py +0 -0
  501. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_executor.py +0 -0
  502. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_executor_windows.py +0 -0
  503. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_reply_drain_command.py +0 -0
  504. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_reply_drain_supervisor.py +0 -0
  505. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_reply_outbox.py +0 -0
  506. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_reply_transport.py +0 -0
  507. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_service_e2e.py +0 -0
  508. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_service_host.py +0 -0
  509. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_service_payload.py +0 -0
  510. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_peer_service_reply.py +0 -0
  511. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_postcompact_recall.py +0 -0
  512. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_process_liveness.py +0 -0
  513. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_protocol_handler.py +0 -0
  514. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_providers_isolation.py +0 -0
  515. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_ratify_command.py +0 -0
  516. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_readme_claims_no_gateway_default.py +0 -0
  517. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_repl_caller_convention.py +0 -0
  518. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_schedule_command.py +0 -0
  519. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_schedule_create_contract.py +0 -0
  520. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_scrub.py +0 -0
  521. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_security.py +0 -0
  522. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_service_app.py +0 -0
  523. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_service_command.py +0 -0
  524. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_service_providers.py +0 -0
  525. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_session_bridge_inject.py +0 -0
  526. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_session_bridge_probe.py +0 -0
  527. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_session_bridge_resolve.py +0 -0
  528. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_session_path_role_gate.py +0 -0
  529. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_shipped_units_match_the_cli.py +0 -0
  530. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_smoke_chat.py +0 -0
  531. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_smoke_one_shot.py +0 -0
  532. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_smoke_phase_5_5.py +0 -0
  533. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_base_hoist.py +0 -0
  534. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_command.py +0 -0
  535. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_identity_stamp.py +0 -0
  536. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_live_pin.py +0 -0
  537. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_mitosis_lineage.py +0 -0
  538. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_tmux_session.py +0 -0
  539. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_spawn_windows_relaunch.py +0 -0
  540. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_subprocess_decode_encoding.py +0 -0
  541. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_timeline_command.py +0 -0
  542. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_tmux_wake_runstate_defer.py +0 -0
  543. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_tmux_wake_session_reanchor.py +0 -0
  544. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_tmux_wake_stale.py +0 -0
  545. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_tmux_wake_submit_verify.py +0 -0
  546. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_token_file_one_parser.py +0 -0
  547. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_token_precedence_explicit_first.py +0 -0
  548. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_unified_token_resolver.py +0 -0
  549. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_version_command.py +0 -0
  550. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_version_consistency.py +0 -0
  551. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_vibe_membrane.py +0 -0
  552. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog.py +0 -0
  553. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_dm_wake.py +0 -0
  554. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  555. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  556. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_emit_health.py +0 -0
  557. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_install_flags.py +0 -0
  558. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_model_rung.py +0 -0
  559. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/tests/test_watchdog_stale_peers.py +0 -0
  560. {swarph_cli-0.72.35 → swarph_cli-0.72.37}/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.35
3
+ Version: 0.72.37
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.35"
7
+ version = "0.72.37"
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.35"
19
+ __version__ = "0.72.37"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -38,6 +38,8 @@ import urllib.request
38
38
  from dataclasses import dataclass
39
39
  from typing import Any, Callable, Optional, Protocol
40
40
 
41
+ from swarph_cli.redact import encode_outbound_json
42
+
41
43
 
42
44
  @dataclass
43
45
  class BackendResult:
@@ -510,7 +512,7 @@ class TypedHttpBackend:
510
512
  body = _request_body(prompt, system)
511
513
  req = urllib.request.Request(
512
514
  endpoint,
513
- data=json.dumps(body).encode(),
515
+ data=encode_outbound_json(body, caller="bench.SystemOneBackend.generate"),
514
516
  headers={"Content-Type": "application/json"},
515
517
  method="POST",
516
518
  )
@@ -626,7 +628,7 @@ class HttpBackend:
626
628
  scheme = self.auth.get("scheme") or ""
627
629
  token = f"{scheme} {value}".strip() if scheme else value
628
630
  headers[self.auth["header"]] = token
629
- raw_body = json.dumps(body).encode()
631
+ raw_body = encode_outbound_json(body, caller="bench.HttpBackend.generate")
630
632
  self.calls += 1
631
633
  if self.transport is not None:
632
634
  status, reason, raw = self.transport(endpoint, headers, raw_body)
@@ -919,7 +921,7 @@ class DecisionsBackend:
919
921
  scheme = self.auth.get("scheme") or ""
920
922
  token = f"{scheme} {value}".strip() if scheme else value
921
923
  headers[self.auth["header"]] = token
922
- raw_body = json.dumps(body).encode()
924
+ raw_body = encode_outbound_json(body, caller="bench.DecisionsBackend.generate")
923
925
  self.calls += 1
924
926
  if self.transport is not None:
925
927
  status, reason, raw = self.transport(endpoint, headers, raw_body)
@@ -43,6 +43,7 @@ from swarph_cli.commands.mesh import (
43
43
  _resolve_self_name,
44
44
  _resolve_token,
45
45
  )
46
+ from swarph_cli.redact import redact_text, redact_value, encode_outbound_json
46
47
 
47
48
 
48
49
  _SENTINEL = object()
@@ -51,7 +52,7 @@ _SENTINEL = object()
51
52
  # ── HTTP: PATCH (mesh.py has GET + POST; the board needs PATCH for move/link) ──
52
53
 
53
54
  def _patch_json(url: str, body: dict, token: str, *, timeout: float = 10.0) -> tuple[int, dict]:
54
- data = json.dumps(body).encode("utf-8")
55
+ data = encode_outbound_json(body, caller="board._patch_json")
55
56
  req = urllib.request.Request(
56
57
  url, data=data, method="PATCH",
57
58
  headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
@@ -89,11 +90,12 @@ def _normalize_due_at(value: str) -> str:
89
90
 
90
91
 
91
92
  def _card_add_payload(actor, project_id, title, *, body=None, ai2=False, priority=0,
92
- labels=None, due_at=None) -> dict:
93
+ labels=None, due_at=None, opt_out=False) -> dict:
94
+ title = redact_text(title, opt_out=opt_out, caller="board.cards.add")
93
95
  p = {"actor": actor, "project_id": int(project_id), "title": title,
94
96
  "ai2": bool(ai2), "priority": int(priority)}
95
97
  if body:
96
- p["body"] = body
98
+ p["body"] = redact_text(body, opt_out=opt_out, caller="board.cards.add")
97
99
  if labels:
98
100
  p["labels"] = list(labels)
99
101
  if due_at is not None:
@@ -151,7 +153,7 @@ def _format_obligations(data: dict) -> str:
151
153
 
152
154
 
153
155
  def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None,
154
- priority=None) -> dict:
156
+ priority=None, opt_out=False) -> dict:
155
157
  """#191: the edit patch — title and/or body, the two fields that made a
156
158
  card's text write-once until the gateway grew them (BoardCardPatch).
157
159
 
@@ -173,9 +175,9 @@ def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None,
173
175
  """
174
176
  patch = {"actor": actor}
175
177
  if title is not None:
176
- patch["title"] = title
178
+ patch["title"] = redact_text(title, opt_out=opt_out, caller="board.cards.edit")
177
179
  if body is not None:
178
- patch["body"] = body
180
+ patch["body"] = redact_text(body, opt_out=opt_out, caller="board.cards.edit")
179
181
  if due_at is not _SENTINEL:
180
182
  # "" is the gateway's CLEAR sentinel; None means "not mentioned" and the
181
183
  # write is skipped while the old date is echoed back (#901).
@@ -190,7 +192,7 @@ def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None,
190
192
  return patch
191
193
 
192
194
 
193
- def _write_card_preimage(actor, gateway, card_id, card, patch) -> Path:
195
+ def _write_card_preimage(actor, gateway, card_id, card, patch, *, opt_out=False) -> Path:
194
196
  """Append the card's CURRENT title and body to a local log BEFORE an edit
195
197
  replaces them. Returns the path written.
196
198
 
@@ -213,10 +215,10 @@ def _write_card_preimage(actor, gateway, card_id, card, patch) -> Path:
213
215
  "gateway": gateway,
214
216
  "card": card_id,
215
217
  "body_version": card.get("body_version"),
216
- "prev_title": card.get("title"),
217
- "prev_body": card.get("body"),
218
- "new_title": patch.get("title"),
219
- "new_body": patch.get("body"),
218
+ "prev_title": redact_text(card.get("title"), opt_out=opt_out, caller="board.cards.edit"),
219
+ "prev_body": redact_text(card.get("body"), opt_out=opt_out, caller="board.cards.edit"),
220
+ "new_title": redact_text(patch.get("title"), opt_out=opt_out, caller="board.cards.edit"),
221
+ "new_body": redact_text(patch.get("body"), opt_out=opt_out, caller="board.cards.edit"),
220
222
  }
221
223
  path = card_preimage_path()
222
224
  path.parent.mkdir(parents=True, exist_ok=True)
@@ -251,10 +253,11 @@ def _apply_label(current: list, action: str, label: str) -> list:
251
253
  return [x for x in cur if x != lab]
252
254
 
253
255
 
254
- def _project_add_payload(actor, slug, title, *, goal=None) -> dict:
256
+ def _project_add_payload(actor, slug, title, *, goal=None, opt_out: bool = False) -> dict:
257
+ title = redact_text(title, opt_out=opt_out, caller="board.projects")
255
258
  p = {"actor": actor, "slug": slug, "title": title}
256
259
  if goal:
257
- p["goal"] = goal
260
+ p["goal"] = redact_text(goal, opt_out=opt_out, caller="board.projects")
258
261
  return p
259
262
 
260
263
 
@@ -546,7 +549,8 @@ def _say_line(resp: dict, card_id, to_node: str, *, reason: Optional[str] = None
546
549
 
547
550
 
548
551
  def _card_say_payload(from_node: str, to_node: str, kind: str, content: str,
549
- thread_uuid: str) -> dict:
552
+ thread_uuid: str, *, opt_out=False) -> dict:
553
+ content = redact_text(content, opt_out=opt_out, caller="board.cards.say")
550
554
  return {
551
555
  "from_node": from_node,
552
556
  "to_node": to_node,
@@ -726,7 +730,7 @@ def _json_flag(flag: str, value: str):
726
730
 
727
731
  def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=None,
728
732
  done=None, accept=None, accept_json=None, kind="action",
729
- timeout_hours=None, task=None, task_needs=None) -> dict:
733
+ timeout_hours=None, task=None, task_needs=None, opt_out=False) -> dict:
730
734
  """POST /board/cards/{id}/ask body. None = not mentioned = key ABSENT (#191's
731
735
  rule; test_532 pins that a null `accept` on the wire would overwrite the
732
736
  gateway's own normalization). No holder → `requested`; `me` → the caller's
@@ -736,6 +740,7 @@ def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=N
736
740
  if accept is not None and accept_json is not None:
737
741
  raise ValueError("pass --accept or --accept-json, not both — "
738
742
  "one check per row")
743
+ what = redact_text(what, opt_out=opt_out, caller="board.cards.ask")
739
744
  p = {"what": what, "created_by": self_name, "kind": kind}
740
745
  if holder is not None:
741
746
  p["holder"] = self_name if holder == "me" else holder
@@ -746,11 +751,11 @@ def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=N
746
751
  if hours is not None:
747
752
  p["hours"] = hours
748
753
  if done is not None:
749
- p["done"] = done
754
+ p["done"] = redact_text(done, opt_out=opt_out, caller="board.cards.ask")
750
755
  if accept is not None:
751
- p["accept"] = accept
756
+ p["accept"] = redact_text(accept, opt_out=opt_out, caller="board.cards.ask")
752
757
  if accept_json is not None:
753
- p["accept_json"] = accept_json
758
+ p["accept_json"] = redact_value(accept_json, opt_out=opt_out, caller="board.cards.ask")
754
759
  if timeout_hours is not None:
755
760
  p["timeout_hours"] = timeout_hours
756
761
  if task is not None:
@@ -830,7 +835,7 @@ def _ask_line(d) -> str:
830
835
  return line
831
836
 
832
837
 
833
- def _confirm_flow_payload(actor: str, holders, what=None) -> dict:
838
+ def _confirm_flow_payload(actor: str, holders, what=None, *, opt_out=False) -> dict:
834
839
  """#801: {actor, holders: {step: peer}, what}. `--holder STEP=PEER` is parsed here so a
835
840
  malformed override is refused before the request, naming the form."""
836
841
  body = {"actor": actor}
@@ -844,7 +849,7 @@ def _confirm_flow_payload(actor: str, holders, what=None) -> dict:
844
849
  out[step] = peer
845
850
  body["holders"] = out
846
851
  if what:
847
- body["what"] = what
852
+ body["what"] = redact_text(what, opt_out=opt_out, caller="board.cards.confirm-flow")
848
853
  return body
849
854
 
850
855
 
@@ -924,25 +929,47 @@ def _move_line(x) -> str:
924
929
  return line
925
930
 
926
931
 
927
- def _close_payload(outcome, evidence=None, evidence_json=None) -> dict:
932
+ def _close_payload(outcome, evidence=None, evidence_json=None, *, opt_out=False,
933
+ usage=None) -> dict:
928
934
  """POST /board/obligations/{id}/close body (#1068): --evidence and/or
929
935
  --evidence-json, at least one. The CLI cannot know the row's stored
930
936
  accept, so it sends what it was given — the gateway requires the map
931
- for a JSON pass, the string everywhere else."""
937
+ for a JSON pass, the string everywhere else.
938
+
939
+ Card #1277: usage is attached on every close. When the caller does not
940
+ pass it, it is read from this cell's transcript (or left unknown)."""
932
941
  if evidence is None and evidence_json is None:
933
942
  raise ValueError("nothing observed — pass --evidence and/or "
934
943
  "--evidence-json")
935
- p = {"outcome": outcome}
944
+ if usage is None:
945
+ from swarph_cli.commands.board_usage import read_close_usage
946
+ usage = read_close_usage()
947
+ p = {"outcome": outcome, "usage": usage}
936
948
  if evidence is not None:
937
- p["evidence"] = evidence
949
+ p["evidence"] = redact_text(evidence, opt_out=opt_out, caller="board.obligations.close")
938
950
  if evidence_json is not None:
939
- p["evidence_json"] = evidence_json
951
+ p["evidence_json"] = redact_value(evidence_json, opt_out=opt_out, caller="board.obligations.close")
940
952
  return p
941
953
 
942
954
 
955
+ def _obligation_taken_at(gw: str, token: str, obligation_id: int):
956
+ """The row's taken_at, or None when the list does not show it.
957
+
958
+ The transcript sum is bounded by this instant. Without it the close
959
+ sends unknown rather than summing some other window."""
960
+ status, body = _http_get_json(
961
+ f"{gw}/board/obligations?obligation_id={obligation_id}", token)
962
+ if not (status and 200 <= status < 300) or not isinstance(body, dict):
963
+ return None
964
+ for item in body.get("obligations") or []:
965
+ if isinstance(item, dict) and item.get("id") == obligation_id:
966
+ return item.get("taken_at")
967
+ return None
968
+
969
+
943
970
  def _amend_payload(step=None, holder=None, accept=None, hours=None,
944
971
  needs_add=None, needs_remove=None, needs=None,
945
- accept_json=None) -> dict:
972
+ accept_json=None, *, opt_out=False) -> dict:
946
973
  """PATCH /board/obligations/{id}/amend body. None = not mentioned; an empty
947
974
  `holder` is a REAL value (clears → `requested`), so the test is
948
975
  `is not None`, never truthiness (#191). `needs` is the contract's signed
@@ -953,6 +980,10 @@ def _amend_payload(step=None, holder=None, accept=None, hours=None,
953
980
  if accept is not None and accept_json is not None:
954
981
  raise ValueError("pass --accept or --accept-json, not both — "
955
982
  "one check per row")
983
+ if accept is not None:
984
+ accept = redact_text(accept, opt_out=opt_out, caller="board.obligations")
985
+ if accept_json is not None:
986
+ accept_json = redact_value(accept_json, opt_out=opt_out, caller="board.obligations")
956
987
  signed = _step_list(needs)
957
988
  add = _step_list(needs_add) + [x for x in signed if not x.startswith("-")]
958
989
  rem = _step_list(needs_remove) + [x[1:] for x in signed if x.startswith("-")]
@@ -1268,6 +1299,13 @@ def _build_parser() -> argparse.ArgumentParser:
1268
1299
  ow.add_argument("--reason", required=True,
1269
1300
  help="why the row is withdrawn; whitespace is refused")
1270
1301
  ow.add_argument("--json", action="store_true"); _add_common(ow)
1302
+ cost = top.add_parser(
1303
+ "cost",
1304
+ help="tokens by job, the perfect-run rate, and cost per accepted outcome")
1305
+ cost.add_argument("--card", type=int, default=None)
1306
+ cost.add_argument("--since", default=None, metavar="DATE")
1307
+ cost.add_argument("--json", action="store_true")
1308
+ _add_common(cost)
1271
1309
  return p
1272
1310
 
1273
1311
 
@@ -1313,9 +1351,27 @@ def run_board(argv: list[str]) -> int:
1313
1351
  st, d = _http_get_json(f"{gw}/board/projects", token)
1314
1352
  return _out(st, d, _format_projects, aj)
1315
1353
  if args.command == "add":
1316
- st, d = _post_json(f"{gw}/board/projects", _project_add_payload(self_name, args.slug, args.title, goal=args.goal), token)
1354
+ st, d = _post_json(f"{gw}/board/projects", _project_add_payload(self_name, args.slug, args.title, goal=args.goal, opt_out=getattr(args, "no_redact", False)), token)
1317
1355
  return _out(st, d, lambda x: f"created project #{x.get('id')} ({x.get('slug')})", aj)
1318
1356
 
1357
+ if args.group == "cost":
1358
+ from swarph_cli.commands.board_cost import format_cost
1359
+ query = []
1360
+ if getattr(args, "card", None) is not None:
1361
+ query.append(f"card={args.card}")
1362
+ if getattr(args, "since", None):
1363
+ query.append("since=" + args.since)
1364
+ url = f"{gw}/board/cost" + (("?" + "&".join(query)) if query else "")
1365
+ st, d = _http_get_json(url, token)
1366
+ rates = None
1367
+ if st and 200 <= st < 300:
1368
+ try:
1369
+ from swarph_shared.llm_cost import fetch_price_base
1370
+ rates = fetch_price_base()
1371
+ except Exception:
1372
+ rates = None
1373
+ return _out(st, d, lambda body: format_cost(body, rates), aj)
1374
+
1319
1375
  if args.group == "obligations":
1320
1376
  if args.command == "close":
1321
1377
  # #562's CLI half: the explicit close act. The gateway guards the
@@ -1332,7 +1388,12 @@ def run_board(argv: list[str]) -> int:
1332
1388
  try:
1333
1389
  evidence_json = (_json_flag("--evidence-json", args.evidence_json)
1334
1390
  if args.evidence_json is not None else None)
1335
- payload = _close_payload(args.outcome, evidence, evidence_json)
1391
+ taken_at = _obligation_taken_at(gw, token, args.id)
1392
+ from swarph_cli.commands.board_usage import read_close_usage
1393
+ usage = read_close_usage(agent=self_name, taken_at=taken_at)
1394
+ payload = _close_payload(args.outcome, evidence, evidence_json,
1395
+ opt_out=getattr(args, "no_redact", False),
1396
+ usage=usage)
1336
1397
  except ValueError as exc:
1337
1398
  print(f"swarph board obligations close: {exc}", file=sys.stderr)
1338
1399
  return 2
@@ -1362,8 +1423,9 @@ def run_board(argv: list[str]) -> int:
1362
1423
  st, d = _post_json(f"{gw}/board/obligations/{args.id}/take", {}, token)
1363
1424
  return _out(st, d, _obligation_act_line, aj)
1364
1425
  if args.command == "decline":
1426
+ why = redact_text(args.why, opt_out=getattr(args, "no_redact", False), caller="board.obligations")
1365
1427
  st, d = _post_json(f"{gw}/board/obligations/{args.id}/decline",
1366
- {"why": args.why}, token)
1428
+ {"why": why}, token)
1367
1429
  return _out(st, d, _obligation_act_line, aj)
1368
1430
  if args.command == "amend":
1369
1431
  try:
@@ -1373,7 +1435,8 @@ def run_board(argv: list[str]) -> int:
1373
1435
  accept=args.accept, hours=args.hours,
1374
1436
  needs_add=args.needs_add,
1375
1437
  needs_remove=args.needs_remove, needs=args.needs,
1376
- accept_json=accept_json)
1438
+ accept_json=accept_json,
1439
+ opt_out=getattr(args, "no_redact", False))
1377
1440
  except ValueError as exc:
1378
1441
  print(f"swarph board obligations amend: {exc}", file=sys.stderr)
1379
1442
  return 2
@@ -1385,6 +1448,7 @@ def run_board(argv: list[str]) -> int:
1385
1448
  print("swarph board obligations withdraw: --reason is "
1386
1449
  "whitespace-only", file=sys.stderr)
1387
1450
  return 2
1451
+ reason = redact_text(reason, opt_out=getattr(args, "no_redact", False), caller="board.obligations")
1388
1452
  st, d = _post_json(
1389
1453
  f"{gw}/board/obligations/{args.id}/withdraw",
1390
1454
  {"reason": reason}, token)
@@ -1441,7 +1505,7 @@ def run_board(argv: list[str]) -> int:
1441
1505
  st, d = _post_json(f"{gw}/board/cards", _card_add_payload(
1442
1506
  self_name, pid, title, body=body, ai2=args.ai2,
1443
1507
  priority=args.priority, labels=getattr(args, "labels", None),
1444
- due_at=args.due), token)
1508
+ due_at=args.due, opt_out=getattr(args, "no_redact", False)), token)
1445
1509
  return _out(st, d, lambda x: f"created card #{x.get('id')} [{x.get('stage')}] (stage defaults to proposed — use `cards move` to advance)", aj)
1446
1510
  if args.command == "label":
1447
1511
  # READ the current set before replacing it. If the GET fails we must
@@ -1491,7 +1555,8 @@ def run_board(argv: list[str]) -> int:
1491
1555
  try:
1492
1556
  patch = _card_edit_payload(
1493
1557
  self_name, title_text, body_text, due_at=args.due,
1494
- project_id=project_id, priority=args.priority)
1558
+ project_id=project_id, priority=args.priority,
1559
+ opt_out=getattr(args, "no_redact", False))
1495
1560
  except ValueError as exc:
1496
1561
  print(f"swarph board cards edit: {exc}", file=sys.stderr)
1497
1562
  return 2
@@ -1508,7 +1573,8 @@ def run_board(argv: list[str]) -> int:
1508
1573
  f"an unrecoverable one", file=sys.stderr)
1509
1574
  return 1
1510
1575
  try:
1511
- pre = _write_card_preimage(self_name, gw, args.id, cur, patch)
1576
+ pre = _write_card_preimage(self_name, gw, args.id, cur, patch,
1577
+ opt_out=getattr(args, "no_redact", False))
1512
1578
  except OSError as exc:
1513
1579
  print(f"swarph board cards edit: cannot write the pre-image log "
1514
1580
  f"({exc}) — refusing to write, for the same reason: the "
@@ -1541,7 +1607,7 @@ def run_board(argv: list[str]) -> int:
1541
1607
  # the row in one gateway call, or neither.
1542
1608
  if args.holder is not None and args.holder_flag is not None:
1543
1609
  print("swarph board cards ask: holder given twice (positional and "
1544
- "--holder) — pass one", file=sys.stderr)
1610
+ "--holder) — pass one", file=sys.stderr)
1545
1611
  return 2
1546
1612
  holder = args.holder_flag or args.holder
1547
1613
  if holder is None and args.step is None:
@@ -1562,7 +1628,8 @@ def run_board(argv: list[str]) -> int:
1562
1628
  done=args.done, accept=args.accept,
1563
1629
  accept_json=accept_json, kind=args.kind,
1564
1630
  timeout_hours=args.timeout_hours,
1565
- task=args.task, task_needs=args.task_needs)
1631
+ task=args.task, task_needs=args.task_needs,
1632
+ opt_out=getattr(args, "no_redact", False))
1566
1633
  except ValueError as exc:
1567
1634
  print(f"swarph board cards ask: {exc}", file=sys.stderr)
1568
1635
  return 2
@@ -1570,7 +1637,8 @@ def run_board(argv: list[str]) -> int:
1570
1637
  return _out(st, d, _ask_line, args.json)
1571
1638
  if args.command == "confirm-flow":
1572
1639
  try:
1573
- body = _confirm_flow_payload(self_name, args.holder, args.what)
1640
+ body = _confirm_flow_payload(self_name, args.holder, args.what,
1641
+ opt_out=getattr(args, "no_redact", False))
1574
1642
  except ValueError as e:
1575
1643
  print(f"swarph board cards confirm-flow: {e}", file=sys.stderr)
1576
1644
  return 2
@@ -1614,7 +1682,8 @@ def run_board(argv: list[str]) -> int:
1614
1682
  return 1
1615
1683
  st, d = _post_json(
1616
1684
  f"{gw}/messages",
1617
- _card_say_payload(self_name, to_node, args.kind, content, thread_uuid),
1685
+ _card_say_payload(self_name, to_node, args.kind, content, thread_uuid,
1686
+ opt_out=getattr(args, "no_redact", False)),
1618
1687
  token,
1619
1688
  )
1620
1689
  # A 403 here carries the gateway's explanation that attaching PUBLISHES
@@ -0,0 +1,199 @@
1
+ """Card #1277 — cost per accepted outcome, from recorded usage only.
2
+
3
+ A row whose usage is unknown is counted and excluded from the token
4
+ totals. It is never treated as zero.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ _ALWAYS = ("input_tokens", "output_tokens", "cache_read_tokens")
9
+ _JOBS = ("execute", "grade", "advise", "rework", "other")
10
+ _GRADE = {"validate", "plan-review", "second-seat", "spec-review"}
11
+
12
+
13
+ def job_of(row) -> str | None:
14
+ if row.get("rework") or row.get("step") == "rework":
15
+ return "rework"
16
+ step = row.get("step")
17
+ if step is None or step == "":
18
+ return "other"
19
+ if step == "build":
20
+ return "execute"
21
+ if step in _GRADE:
22
+ return "grade"
23
+ if step in {"advise", "advisor", "pre-read"} or "advise" in step:
24
+ return "advise"
25
+ return None
26
+
27
+
28
+ def _as_int(value):
29
+ if isinstance(value, bool) or not isinstance(value, int):
30
+ return None
31
+ return value
32
+
33
+
34
+ def token_total(usage):
35
+ """Sum the fields that were read. Unknown is not zero.
36
+
37
+ Thinking is a breakdown of output_tokens and is not added again.
38
+ A cache split that was read is the cache total. A flat
39
+ cache_creation with no split is the total instead. Having neither
40
+ makes the row unknown."""
41
+ if not isinstance(usage, dict):
42
+ return None
43
+ total = 0
44
+ for name in _ALWAYS:
45
+ value = _as_int(usage.get(name, "unknown"))
46
+ if value is None:
47
+ return None
48
+ total += value
49
+ one_h = _as_int(usage.get("cache_creation_1h_tokens", "unknown"))
50
+ five = _as_int(usage.get("cache_creation_5m_tokens", "unknown"))
51
+ unsplit = _as_int(usage.get("cache_creation_unsplit_tokens", "unknown"))
52
+ if one_h is not None and five is not None:
53
+ return total + one_h + five
54
+ if unsplit is not None and one_h is None and five is None:
55
+ return total + unsplit
56
+ return None
57
+
58
+
59
+ def row_cost(usage, rates):
60
+ """USD from llm_cost.compute_cost, or None when a price row or a
61
+ reading is missing. Unknown tokens are not passed in as zero."""
62
+ if token_total(usage) is None or not isinstance(usage, dict):
63
+ return None
64
+ model = usage.get("model")
65
+ price = (rates or {}).get(model) if isinstance(rates, dict) else None
66
+ if price is None:
67
+ return None
68
+ from swarph_shared.llm_cost import TokenUsage, compute_cost
69
+ one_h = _as_int(usage.get("cache_creation_1h_tokens", "unknown"))
70
+ five = _as_int(usage.get("cache_creation_5m_tokens", "unknown"))
71
+ unsplit = _as_int(usage.get("cache_creation_unsplit_tokens", "unknown"))
72
+ if one_h is not None and five is not None:
73
+ parsed = TokenUsage(
74
+ input_tokens=usage["input_tokens"],
75
+ output_tokens=usage["output_tokens"],
76
+ cache_read_tokens=usage["cache_read_tokens"],
77
+ cache_creation_1h_tokens=one_h,
78
+ cache_creation_5m_tokens=five,
79
+ )
80
+ elif unsplit is not None:
81
+ parsed = TokenUsage(
82
+ input_tokens=usage["input_tokens"],
83
+ output_tokens=usage["output_tokens"],
84
+ cache_read_tokens=usage["cache_read_tokens"],
85
+ cache_creation_unsplit_tokens=unsplit,
86
+ )
87
+ else:
88
+ return None
89
+ return compute_cost(parsed, price)
90
+
91
+
92
+ def _window_label(window) -> str:
93
+ window = window or {}
94
+ card = window.get("card")
95
+ since = window.get("since") or "-"
96
+ until = window.get("until") or "-"
97
+ where = f"card #{card}" if card else "all cards"
98
+ return f"{where} since {since} until {until}"
99
+
100
+
101
+ def format_cost(report: dict, rates=None) -> str:
102
+ closes = report.get("closes") or []
103
+ later = report.get("later_fix") or {}
104
+ by_card: dict = {}
105
+ for row in closes:
106
+ by_card.setdefault(row.get("card_id"), []).append(row)
107
+ if rates is None:
108
+ rates = report.get("rates")
109
+ lines = [f"window: {_window_label(report.get('window'))}"]
110
+ unknown_rows = 0
111
+ usd_parts = []
112
+ usd_blocked = False
113
+ underestimated = False
114
+ done_known = []
115
+ perfect_known = []
116
+ done = 0
117
+ perfect = 0
118
+ for card_id, rows in by_card.items():
119
+ stage = rows[0].get("stage") or "-"
120
+ fixes = later.get(str(card_id)) or later.get(card_id) or []
121
+ fails = sum(1 for row in rows if row.get("outcome") == "fail")
122
+ reworks = sum(1 for row in rows if row.get("rework") or row.get("step") == "rework")
123
+ fix_label = (" later-fix=" + ",".join(f"#{n}" for n in fixes)) if fixes else ""
124
+ lines.append(f"card #{card_id} stage={stage}{fix_label}")
125
+ buckets = {job: {"tokens": 0, "known": 0, "unknown": 0} for job in _JOBS}
126
+ card_known = 0
127
+ card_unknown = 0
128
+ for row in rows:
129
+ total = token_total(row.get("usage"))
130
+ job = job_of(row)
131
+ if total is None:
132
+ unknown_rows += 1
133
+ card_unknown += 1
134
+ if job in buckets:
135
+ buckets[job]["unknown"] += 1
136
+ continue
137
+ card_known += total
138
+ if job in buckets:
139
+ buckets[job]["tokens"] += total
140
+ buckets[job]["known"] += 1
141
+ for job in _JOBS:
142
+ bucket = buckets[job]
143
+ lines.append(
144
+ f" {job}: {bucket['tokens']} tokens over {bucket['known']} "
145
+ f"known rows (unknown {bucket['unknown']})")
146
+ lines.append(f" rows: {len(rows)} FAIL: {fails} reworks: {reworks}")
147
+ if stage == "done":
148
+ for row in rows:
149
+ priced = row_cost(row.get("usage"), rates)
150
+ if priced is None:
151
+ usd_blocked = True
152
+ else:
153
+ usd_parts.append(priced.cost_usd)
154
+ underestimated = underestimated or priced.underestimated
155
+ is_done = stage == "done"
156
+ is_perfect = is_done and fails == 0 and reworks == 0 and not fixes
157
+ if is_done:
158
+ done += 1
159
+ if card_unknown == 0:
160
+ done_known.append(card_known)
161
+ if is_perfect:
162
+ perfect += 1
163
+ if card_unknown == 0:
164
+ perfect_known.append(card_known)
165
+ lines.append(f"unknown rows: {unknown_rows}")
166
+ known_sum = sum(done_known)
167
+ if done and len(done_known) == done:
168
+ lines.append(
169
+ f"tokens per card done: {known_sum}/{done} "
170
+ f"(known tokens, unknown rows excluded)")
171
+ lines.append(
172
+ f"cost per accepted outcome: {known_sum}/{done} "
173
+ f"tokens per done card (unknown rows: {unknown_rows})")
174
+ else:
175
+ lines.append(
176
+ "tokens per card done: unknown "
177
+ f"({len(done_known)} of {done} done cards fully read)")
178
+ lines.append(
179
+ "cost per accepted outcome: unknown "
180
+ f"({len(done_known)} of {done} done cards fully read, "
181
+ f"unknown rows: {unknown_rows})")
182
+ lines.append(f"perfect-run rate: {perfect}/{done}")
183
+ if perfect and len(perfect_known) == perfect:
184
+ lines.append(
185
+ f"cost per perfect run: {sum(perfect_known)}/{perfect} "
186
+ f"tokens per perfect card (unknown rows: {unknown_rows})")
187
+ else:
188
+ lines.append(f"cost per perfect run: no perfect runs ({perfect})")
189
+ if usd_blocked or not usd_parts:
190
+ lines.append("usd: unknown")
191
+ else:
192
+ total_usd = sum(usd_parts)
193
+ shown = int(total_usd) if total_usd == int(total_usd) else total_usd
194
+ note = " (unsplit cache priced at the 5m rate)" if underestimated else ""
195
+ lines.append(f"usd: {shown}{note}")
196
+ lines.append(
197
+ "sub-agent spend: included in each close "
198
+ "(that session's subagents, each response once)")
199
+ return "\n".join(lines)