swarph-cli 0.72.16__tar.gz → 0.72.17__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 (505) hide show
  1. {swarph_cli-0.72.16/src/swarph_cli.egg-info → swarph_cli-0.72.17}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/backends.py +341 -0
  5. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/providers.py +7 -3
  6. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/bench.py +21 -5
  7. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/schedule.py +10 -0
  8. {swarph_cli-0.72.16 → swarph_cli-0.72.17/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  9. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
  10. swarph_cli-0.72.17/tests/test_1083_openai_decisions.py +376 -0
  11. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_323_mistral_backend.py +12 -1
  12. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_schedule_command.py +31 -0
  13. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/LICENSE +0 -0
  14. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/README.md +0 -0
  15. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/setup.cfg +0 -0
  16. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/__main__.py +0 -0
  17. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/__init__.py +0 -0
  18. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/claude_cli.py +0 -0
  19. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/codex_cli.py +0 -0
  20. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/pack.py +0 -0
  21. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/prices.py +0 -0
  22. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/quality.py +0 -0
  23. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/refresh_prices.py +0 -0
  24. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/rules.py +0 -0
  25. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/runner.py +0 -0
  26. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/signal_score.py +0 -0
  27. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/bench/validate.py +0 -0
  28. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/caller.py +0 -0
  29. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/__init__.py +0 -0
  30. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/harden.py +0 -0
  31. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/lineage.py +0 -0
  32. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/liveness.py +0 -0
  33. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/manifest.py +0 -0
  34. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/paths.py +0 -0
  35. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/capture/verify.py +0 -0
  36. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/cell.py +0 -0
  37. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/chain_token.py +0 -0
  38. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/channel.py +0 -0
  39. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/__init__.py +0 -0
  40. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/_content.py +0 -0
  41. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/_display.py +0 -0
  42. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/_gateway_client.py +0 -0
  43. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/add.py +0 -0
  44. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/board.py +0 -0
  45. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/board_merge_check.py +0 -0
  46. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/brain.py +0 -0
  47. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/brain_ask.py +0 -0
  48. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/cell.py +0 -0
  49. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/cell_probe.py +0 -0
  50. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  51. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/channel.py +0 -0
  52. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/chat.py +0 -0
  53. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/codegraph.py +0 -0
  54. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  55. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  56. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/codex_waker.py +0 -0
  57. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/compress.py +0 -0
  58. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/daemon.py +0 -0
  59. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/dreaming.py +0 -0
  60. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/event.py +0 -0
  61. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/gateway.py +0 -0
  62. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/gh_route.py +0 -0
  63. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/group.py +0 -0
  64. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/guide.py +0 -0
  65. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/highlight.py +0 -0
  66. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  67. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/hook_output.py +0 -0
  68. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/hooks.py +0 -0
  69. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/import_session.py +0 -0
  70. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/init.py +0 -0
  71. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  72. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_hook.py +0 -0
  73. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  74. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  75. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  76. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  77. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/lane.py +0 -0
  78. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/mcp_server.py +0 -0
  79. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/memory.py +0 -0
  80. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  81. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/memory_sync.py +0 -0
  82. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/mesh.py +0 -0
  83. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/monitor.py +0 -0
  84. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/okf_links.py +0 -0
  85. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/onboard.py +0 -0
  86. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  87. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  88. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/protocol_handler.py +0 -0
  89. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/ratify.py +0 -0
  90. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/roster.py +0 -0
  91. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  92. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/security.py +0 -0
  93. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/service.py +0 -0
  94. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/spawn.py +0 -0
  95. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/sweep.py +0 -0
  96. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/timeline.py +0 -0
  97. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/triage.py +0 -0
  98. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/version.py +0 -0
  99. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  100. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/commands/watchdog.py +0 -0
  101. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/compress/__init__.py +0 -0
  102. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/compress/levers.py +0 -0
  103. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/compress/marker.py +0 -0
  104. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/compress/verify.py +0 -0
  105. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/console_safe.py +0 -0
  106. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/delivery_queue.py +0 -0
  107. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dm_frame.py +0 -0
  108. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/__init__.py +0 -0
  109. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/candidates.py +0 -0
  110. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/clone.py +0 -0
  111. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/enrich.py +0 -0
  112. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/index_check.py +0 -0
  113. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/notify.py +0 -0
  114. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/organize.py +0 -0
  115. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/probes.py +0 -0
  116. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/report.py +0 -0
  117. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/run.py +0 -0
  118. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/slm.py +0 -0
  119. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/transcripts.py +0 -0
  120. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/dreaming/verify.py +0 -0
  121. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/__init__.py +0 -0
  122. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/feature_registry.py +0 -0
  123. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/lanes_control.py +0 -0
  124. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/schema.sql +0 -0
  125. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/server.py +0 -0
  126. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway/services_control.py +0 -0
  127. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gateway_default.py +0 -0
  128. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/gh_identity.py +0 -0
  129. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/guide/GUIDE.md +0 -0
  130. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/identity.py +0 -0
  131. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/main.py +0 -0
  132. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/multiplexer.py +0 -0
  133. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/__init__.py +0 -0
  134. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/__main__.py +0 -0
  135. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/api.py +0 -0
  136. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/cli.py +0 -0
  137. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/config.py +0 -0
  138. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/operator/fmt.py +0 -0
  139. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/orphan_daemons.py +0 -0
  140. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/pane_probe.py +0 -0
  141. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/parsers/__init__.py +0 -0
  142. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/parsers/claude.py +0 -0
  143. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  144. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  145. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  146. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  147. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  148. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  149. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  150. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  151. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  152. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  153. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  154. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  155. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_executor.py +0 -0
  156. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_executor_windows.py +0 -0
  157. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_reconciliation.py +0 -0
  158. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_reply_outbox.py +0 -0
  159. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_service_host.py +0 -0
  160. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_service_payload.py +0 -0
  161. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_service_reply.py +0 -0
  162. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/peer_staging.py +0 -0
  163. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/query_filters.py +0 -0
  164. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/rowclear.py +0 -0
  165. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/__init__.py +0 -0
  166. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  167. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  168. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  169. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  170. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  171. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/scrub.py +0 -0
  172. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/service/__init__.py +0 -0
  173. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/service/app.py +0 -0
  174. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/service/providers.py +0 -0
  175. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/session_bridge.py +0 -0
  176. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  177. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  178. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/source_text.py +0 -0
  179. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/stall_alert.py +0 -0
  180. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/codex-waker.default +0 -0
  181. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  182. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  183. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  184. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  185. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  186. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  187. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  188. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  189. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  190. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  191. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  192. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  193. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  194. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  195. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  196. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  197. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  198. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/timeline_paths.py +0 -0
  199. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/tokens.py +0 -0
  200. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/__init__.py +0 -0
  201. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/backend.py +0 -0
  202. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/job.py +0 -0
  203. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/owners.json +0 -0
  204. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/router.py +0 -0
  205. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/rows.py +0 -0
  206. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli/triage/sensor.py +0 -0
  207. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  208. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  209. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli.egg-info/requires.txt +0 -0
  210. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/src/swarph_cli.egg-info/top_level.txt +0 -0
  211. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1006_board_publisher.py +0 -0
  212. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1006_session_board.py +0 -0
  213. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1010_strand_only.py +0 -0
  214. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1013_spawn_channel.py +0 -0
  215. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1017_muse_tui.py +0 -0
  216. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1050_surrogate_wake.py +0 -0
  217. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1052_opencode_deaf.py +0 -0
  218. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1059_watchdog_unread_env.py +0 -0
  219. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1061_roster_cli.py +0 -0
  220. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1064_stuck_sweep.py +0 -0
  221. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1065_release.py +0 -0
  222. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1066_quiet_wakes.py +0 -0
  223. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1073_triage.py +0 -0
  224. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1073_triage_board.py +0 -0
  225. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1073_wheel_install.py +0 -0
  226. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_1075_task_cli.py +0 -0
  227. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_124_register_merge.py +0 -0
  228. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_126_pending_from_complete.py +0 -0
  229. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  230. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_138_memory_navigate.py +0 -0
  231. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_139_tool_probe.py +0 -0
  232. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_183_card_anchor.py +0 -0
  233. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_184_notify_honesty.py +0 -0
  234. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_201_win32_bash_refusal.py +0 -0
  235. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_243_onboard_per_peer_token.py +0 -0
  236. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_307_task4_ask_reply.py +0 -0
  237. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  238. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_319_refusal_exit_codes.py +0 -0
  239. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_332_report_organize_findings.py +0 -0
  240. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_356_query_filters.py +0 -0
  241. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_397_gh_identity_router.py +0 -0
  242. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_397_router_preflight.py +0 -0
  243. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_402_one_identity_resolver.py +0 -0
  244. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_432_rowclear.py +0 -0
  245. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_467b_onboard_operator_credential.py +0 -0
  246. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_472_register_caller_binding.py +0 -0
  247. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_484_codex_queue_sink.py +0 -0
  248. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_523_guide.py +0 -0
  249. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_524_conftest_hermeticity.py +0 -0
  250. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_532_accept_flag.py +0 -0
  251. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_535_version_capability.py +0 -0
  252. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_538_signal_bench.py +0 -0
  253. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_546_default_gateway.py +0 -0
  254. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_547_verbs_from_registry.py +0 -0
  255. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_548_gateway_remainder.py +0 -0
  256. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_552_codegraph_gateway.py +0 -0
  257. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_555_relay_row_shape.py +0 -0
  258. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_556_relay_freshness.py +0 -0
  259. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_559_edges_truncated.py +0 -0
  260. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_564_bootstrap_first_peer.py +0 -0
  261. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  262. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_579_register_bootstrap_message.py +0 -0
  263. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_591_cli_step_graph.py +0 -0
  264. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_644_monitor_supervision.py +0 -0
  265. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_648_install_unit.py +0 -0
  266. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_654_reexec.py +0 -0
  267. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_656_owned_slm.py +0 -0
  268. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_684_dreaming_verb.py +0 -0
  269. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_689_pkg_version_order.py +0 -0
  270. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_715_grok_wake.py +0 -0
  271. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_716_timeline_one_path.py +0 -0
  272. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_723_grok_0251.py +0 -0
  273. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  274. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_725_stdio_reconfigure.py +0 -0
  275. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_726_grok_limit_screen.py +0 -0
  276. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_729_arm_check_delivery_gap.py +0 -0
  277. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_729_dm_once.py +0 -0
  278. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_729_wake_watchdog.py +0 -0
  279. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  280. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_735_dm_mcp_tools.py +0 -0
  281. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_739_wake_writer.py +0 -0
  282. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_740_edit_project.py +0 -0
  283. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_762_cards_edit_priority.py +0 -0
  284. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_764_enrich_collapse.py +0 -0
  285. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_801_confirm_flow_cli.py +0 -0
  286. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_802_stage_history.py +0 -0
  287. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_825_codegraph_hook_noise.py +0 -0
  288. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_825_install_posttooluse_only.py +0 -0
  289. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  290. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_829_counterfactual_column.py +0 -0
  291. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_830_hook_binding_version.py +0 -0
  292. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_84_cli_labels.py +0 -0
  293. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_866_doctrine_guide.py +0 -0
  294. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_872_identity_refuses_not_warns.py +0 -0
  295. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_874_source_text.py +0 -0
  296. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  297. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_937_dreaming_notify.py +0 -0
  298. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_938_organize_line_limit.py +0 -0
  299. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_941_bench_backends.py +0 -0
  300. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_941_harness_342.py +0 -0
  301. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_945_same_line_pairing.py +0 -0
  302. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_955_prompt_audit.py +0 -0
  303. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_957_auth_http.py +0 -0
  304. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_959_venv_interpreter.py +0 -0
  305. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_960_channel.py +0 -0
  306. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  307. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_961_cursor_grok_footer.py +0 -0
  308. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_961_loud_stop.py +0 -0
  309. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_963_plugin_manifest.py +0 -0
  310. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_964_channel_cell.py +0 -0
  311. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_965_mesh_wait.py +0 -0
  312. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_975_channel_hop.py +0 -0
  313. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_add_security_gate.py +0 -0
  314. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_add.py +0 -0
  315. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_hash.py +0 -0
  316. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_lib.py +0 -0
  317. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_mcp.py +0 -0
  318. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_skill.py +0 -0
  319. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_tool.py +0 -0
  320. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_artifact_uri.py +0 -0
  321. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_backends.py +0 -0
  322. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_claude_cli.py +0 -0
  323. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_codex_cli.py +0 -0
  324. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_command.py +0 -0
  325. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_pack.py +0 -0
  326. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_prices.py +0 -0
  327. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_quality.py +0 -0
  328. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_runner.py +0 -0
  329. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_bench_validate.py +0 -0
  330. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_board_card_thread.py +0 -0
  331. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_board_command.py +0 -0
  332. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_board_merge_check.py +0 -0
  333. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_board_obligation_close.py +0 -0
  334. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_brain_ask_command.py +0 -0
  335. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_brain_ask_env_diagnosis.py +0 -0
  336. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_brain_ask_identity_explicitness.py +0 -0
  337. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_brain_command.py +0 -0
  338. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_caller_meta_guard.py +0 -0
  339. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_capture_lineage.py +0 -0
  340. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_capture_liveness.py +0 -0
  341. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_capture_manifest.py +0 -0
  342. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_capture_paths.py +0 -0
  343. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_capture_security.py +0 -0
  344. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cell_command_dispatch.py +0 -0
  345. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cell_harden.py +0 -0
  346. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cell_loader.py +0 -0
  347. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cell_selfcheck.py +0 -0
  348. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cell_verify.py +0 -0
  349. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_chain_token.py +0 -0
  350. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_channel_command.py +0 -0
  351. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  352. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_channel_post.py +0 -0
  353. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_channel_wake_policy_filter.py +0 -0
  354. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_chat_command.py +0 -0
  355. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_claude_parser.py +0 -0
  356. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_claude_tmux_template.py +0 -0
  357. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_codegraph_caller_scope.py +0 -0
  358. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_codegraph_command.py +0 -0
  359. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_codegraph_hook.py +0 -0
  360. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_codegraph_mcp.py +0 -0
  361. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_codex_waker.py +0 -0
  362. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_compress_caller_convention.py +0 -0
  363. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_compress_command.py +0 -0
  364. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_compress_levers.py +0 -0
  365. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_compress_marker.py +0 -0
  366. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_compress_verify.py +0 -0
  367. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_console_encoding_e2e.py +0 -0
  368. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_content_composition.py +0 -0
  369. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_copilot_membrane.py +0 -0
  370. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_cursor_membrane.py +0 -0
  371. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_daemon_command.py +0 -0
  372. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_daemon_console_encoding.py +0 -0
  373. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_daemon_delivery.py +0 -0
  374. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_delivery_queue.py +0 -0
  375. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_display_sanitize.py +0 -0
  376. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  377. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_event_emit.py +0 -0
  378. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_feature_to_uri.py +0 -0
  379. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_gateway_brain.py +0 -0
  380. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_gateway_client.py +0 -0
  381. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_gateway_command.py +0 -0
  382. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_gateway_message_idempotency.py +0 -0
  383. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  384. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_group_command.py +0 -0
  385. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_guide_commands_parse.py +0 -0
  386. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_guide_selfcheck.py +0 -0
  387. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_heartbeat_check.py +0 -0
  388. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_highlight_command.py +0 -0
  389. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hook_output.py +0 -0
  390. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_activity_marker.py +0 -0
  391. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_add.py +0 -0
  392. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_bundle.py +0 -0
  393. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_init.py +0 -0
  394. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_legacy_generation_removal.py +0 -0
  395. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_lifecycle.py +0 -0
  396. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_merge.py +0 -0
  397. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_verify.py +0 -0
  398. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  399. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_hooks_win32_command_path.py +0 -0
  400. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_import_command.py +0 -0
  401. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_inbox_identity_provenance.py +0 -0
  402. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_init_command.py +0 -0
  403. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_init_provider_menu.py +0 -0
  404. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_install_codex_hooks.py +0 -0
  405. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_install_hook.py +0 -0
  406. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_install_multiplexer_command.py +0 -0
  407. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_install_postcompact_hook.py +0 -0
  408. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_install_wake_hook.py +0 -0
  409. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_lane_command.py +0 -0
  410. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_main.py +0 -0
  411. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_main_unknown_verb_guard.py +0 -0
  412. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mcp_missing_hint.py +0 -0
  413. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mcp_server.py +0 -0
  414. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_memory_command.py +0 -0
  415. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_memory_emit_snippet.py +0 -0
  416. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_memory_gateway.py +0 -0
  417. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_memory_sync.py +0 -0
  418. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_memory_sync_membrane.py +0 -0
  419. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_command.py +0 -0
  420. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_inbox_mark_read.py +0 -0
  421. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_inbox_sanitize.py +0 -0
  422. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_originate_thread_623.py +0 -0
  423. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_send_unknown_peer.py +0 -0
  424. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_sidecar.py +0 -0
  425. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  426. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  427. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  428. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_meta_edge_identity.py +0 -0
  429. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_monitor_channel_poll.py +0 -0
  430. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_monitor_cli.py +0 -0
  431. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_monitor_daemon_collision.py +0 -0
  432. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_monitor_self_name_verification.py +0 -0
  433. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_monitor_state_split.py +0 -0
  434. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_multiplexer.py +0 -0
  435. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_muse_membrane.py +0 -0
  436. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_muse_own_cli.py +0 -0
  437. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_obligation_close_is_surfaced.py +0 -0
  438. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_okf_links.py +0 -0
  439. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_onboard_command.py +0 -0
  440. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_opencode_membrane.py +0 -0
  441. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_opencode_plugin.py +0 -0
  442. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_operator_me.py +0 -0
  443. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_orphan_daemons.py +0 -0
  444. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_packaged_artifacts_exist.py +0 -0
  445. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_packaged_skills.py +0 -0
  446. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_executor.py +0 -0
  447. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_executor_windows.py +0 -0
  448. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_reply_drain_command.py +0 -0
  449. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_reply_drain_supervisor.py +0 -0
  450. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_reply_outbox.py +0 -0
  451. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_reply_transport.py +0 -0
  452. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_service_e2e.py +0 -0
  453. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_service_host.py +0 -0
  454. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_service_payload.py +0 -0
  455. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_peer_service_reply.py +0 -0
  456. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_postcompact_recall.py +0 -0
  457. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_process_liveness.py +0 -0
  458. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_protocol_handler.py +0 -0
  459. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_providers_isolation.py +0 -0
  460. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_ratify_command.py +0 -0
  461. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_readme_claims_no_gateway_default.py +0 -0
  462. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_repl_caller_convention.py +0 -0
  463. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_schedule_create_contract.py +0 -0
  464. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_scrub.py +0 -0
  465. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_security.py +0 -0
  466. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_service_app.py +0 -0
  467. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_service_command.py +0 -0
  468. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_service_providers.py +0 -0
  469. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_session_bridge_inject.py +0 -0
  470. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_session_bridge_probe.py +0 -0
  471. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_session_bridge_resolve.py +0 -0
  472. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_session_path_role_gate.py +0 -0
  473. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_shipped_units_match_the_cli.py +0 -0
  474. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_smoke_chat.py +0 -0
  475. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_smoke_one_shot.py +0 -0
  476. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_smoke_phase_5_5.py +0 -0
  477. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_base_hoist.py +0 -0
  478. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_command.py +0 -0
  479. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_identity_stamp.py +0 -0
  480. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_live_pin.py +0 -0
  481. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_mitosis_lineage.py +0 -0
  482. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_tmux_session.py +0 -0
  483. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_spawn_windows_relaunch.py +0 -0
  484. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_stall_alert.py +0 -0
  485. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_subprocess_decode_encoding.py +0 -0
  486. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_timeline_command.py +0 -0
  487. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_tmux_wake_runstate_defer.py +0 -0
  488. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_tmux_wake_session_reanchor.py +0 -0
  489. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_tmux_wake_stale.py +0 -0
  490. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_tmux_wake_submit_verify.py +0 -0
  491. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_token_file_one_parser.py +0 -0
  492. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_token_precedence_explicit_first.py +0 -0
  493. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_unified_token_resolver.py +0 -0
  494. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_version_command.py +0 -0
  495. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_version_consistency.py +0 -0
  496. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_vibe_membrane.py +0 -0
  497. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog.py +0 -0
  498. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_dm_wake.py +0 -0
  499. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  500. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  501. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_emit_health.py +0 -0
  502. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_install_flags.py +0 -0
  503. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_model_rung.py +0 -0
  504. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/tests/test_watchdog_stale_peers.py +0 -0
  505. {swarph_cli-0.72.16 → swarph_cli-0.72.17}/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.16
3
+ Version: 0.72.17
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.16"
7
+ version = "0.72.17"
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.16"
19
+ __version__ = "0.72.17"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -22,6 +22,9 @@ Ships these backends:
22
22
  - :class:`RuleBackend` — in-process ``rule:<module:callable>``. No credentials.
23
23
  - :class:`TypedHttpBackend` — POST to a Jev-compatible ``/v1/systemone``
24
24
  (laya-serve). No credentials; per-item latency is the HTTP round trip.
25
+ - :class:`DecisionsBackend` — POST ``/v1/decisions``. Translates a pack's
26
+ ``{state, questions}`` body into the Decisions shape. It does not send
27
+ the pack body unchanged.
25
28
  """
26
29
  from __future__ import annotations
27
30
 
@@ -675,3 +678,341 @@ class HttpBackend:
675
678
  latency_s=round(time.perf_counter() - t0, 6),
676
679
  estimated=tin is None or tout is None,
677
680
  )
681
+
682
+
683
+ # The Decisions endpoint documents one model. ``provider:openai`` puts the
684
+ # registry name in model_id, so the model is not taken from that string.
685
+ _DECISIONS_MODEL = "gpt-6-luna"
686
+ _PREDICATE_TYPES = {"noul", "yes", "yes-no", "predicate"}
687
+
688
+
689
+ def decisions_shape_error(body: Any) -> Optional[str]:
690
+ """Why a POST /v1/decisions body is not the documented shape, or None.
691
+
692
+ A double more permissive than the real API hid live failures before
693
+ (#1077 / #1078). This is the check that double applies.
694
+ """
695
+ if not isinstance(body, dict):
696
+ return "body is not an object"
697
+ questions = body.get("questions")
698
+ if not isinstance(questions, list):
699
+ return "questions is not a list"
700
+ for i, question in enumerate(questions):
701
+ if not isinstance(question, dict):
702
+ return f"questions[{i}] is not an object"
703
+ for field in ("name", "type", "instructions"):
704
+ value = question.get(field)
705
+ if not isinstance(value, str) or not value.strip():
706
+ return f"questions[{i}] lacks {field}"
707
+ qtype = question.get("type")
708
+ if qtype == "predicate" and ("choices" in question or "levels" in question):
709
+ return f"questions[{i}] is a predicate carrying choices or levels"
710
+ if qtype == "score":
711
+ levels = question.get("levels")
712
+ if not isinstance(levels, list) or not levels:
713
+ return f"questions[{i}] is a score without levels"
714
+ for j, level in enumerate(levels):
715
+ if (not isinstance(level, dict)
716
+ or not isinstance(level.get("label"), str)
717
+ or not level["label"]
718
+ or not isinstance(level.get("description"), str)):
719
+ return (
720
+ f"questions[{i}].levels[{j}] is not "
721
+ "{label, description}")
722
+ continue
723
+ if qtype != "choice":
724
+ continue
725
+ choices = question.get("choices")
726
+ if not isinstance(choices, list) or not choices:
727
+ return f"questions[{i}] is a choice without choices"
728
+ for j, choice in enumerate(choices):
729
+ if (not isinstance(choice, dict)
730
+ or not isinstance(choice.get("value"), str)
731
+ or not choice["value"]
732
+ or not isinstance(choice.get("description"), str)):
733
+ return (
734
+ f"questions[{i}].choices[{j}] is not "
735
+ "{value, description}")
736
+ return None
737
+
738
+
739
+ class StrictDecisionsDouble:
740
+ """Fake POST /v1/decisions. A body that fails the documented shape is 400.
741
+
742
+ ``response`` is the JSON object returned when the body is accepted.
743
+ """
744
+
745
+ def __init__(self, response: Optional[dict] = None):
746
+ self.response = response if response is not None else {"answers": []}
747
+ self.bodies: list = []
748
+
749
+ def __call__(self, endpoint, headers, raw_body):
750
+ try:
751
+ body = json.loads(raw_body.decode() or "null")
752
+ except (UnicodeDecodeError, json.JSONDecodeError):
753
+ return 400, "Bad Request", b'{"error": "body is not JSON"}'
754
+ self.bodies.append(body)
755
+ reason = decisions_shape_error(body)
756
+ if reason:
757
+ return 400, "Bad Request", json.dumps({"error": reason}).encode()
758
+ return 200, "OK", json.dumps(self.response).encode()
759
+
760
+
761
+ def _pack_prompt(prompt: str) -> dict:
762
+ try:
763
+ parsed = json.loads(prompt) if isinstance(prompt, str) else None
764
+ except json.JSONDecodeError:
765
+ parsed = None
766
+ if (not isinstance(parsed, dict) or "state" not in parsed
767
+ or not isinstance(parsed.get("questions"), dict)
768
+ or not parsed["questions"]):
769
+ raise ValueError(
770
+ "decisions prompt must be JSON with state and questions")
771
+ return parsed
772
+
773
+
774
+ def _answer_block(parsed: dict) -> tuple[str, dict]:
775
+ questions = parsed["questions"]
776
+ block = questions.get("answer")
777
+ if isinstance(block, dict):
778
+ return "answer", block
779
+ name, block = next(iter(questions.items()))
780
+ if not isinstance(block, dict):
781
+ raise ValueError("decisions question is not an object")
782
+ return str(name), block
783
+
784
+
785
+ def _translate_question(name: str, block: dict) -> dict:
786
+ raw_type = block.get("type")
787
+ if raw_type in _PREDICATE_TYPES:
788
+ out_type = "predicate"
789
+ else:
790
+ out_type = raw_type
791
+ question: dict = {"name": name, "type": out_type}
792
+ instructions = block.get("instructions")
793
+ if isinstance(instructions, str):
794
+ question["instructions"] = instructions
795
+ if out_type == "choice" and isinstance(block.get("criteria"), dict):
796
+ question["choices"] = [
797
+ {
798
+ "value": key if isinstance(key, str) else str(key),
799
+ "description": val if isinstance(val, str) else str(val),
800
+ }
801
+ for key, val in block["criteria"].items()
802
+ ]
803
+ if out_type == "score" and isinstance(block.get("levels"), list):
804
+ question["levels"] = block["levels"]
805
+ return question
806
+
807
+
808
+ def _carries_image(node: Any) -> bool:
809
+ """True when a pack value is an image part. Packs are text only."""
810
+ if isinstance(node, dict):
811
+ kind = node.get("type")
812
+ if kind in {"input_image", "image", "image_url"}:
813
+ return True
814
+ if "image_url" in node or "input_image" in node:
815
+ return True
816
+ return any(_carries_image(value) for value in node.values())
817
+ if isinstance(node, list):
818
+ return any(_carries_image(value) for value in node)
819
+ return False
820
+
821
+
822
+ def decisions_request(prompt: str) -> tuple[dict, dict]:
823
+ """Pack prompt -> (original pack object, POST /v1/decisions body)."""
824
+ parsed = _pack_prompt(prompt)
825
+ if _carries_image(parsed.get("state")) or _carries_image(parsed.get("questions")):
826
+ raise ValueError(
827
+ "decisions input carries an image part; packs are text only")
828
+ name, block = _answer_block(parsed)
829
+ return parsed, {
830
+ "model": _DECISIONS_MODEL,
831
+ "input": json.dumps(parsed["state"]),
832
+ "questions": [_translate_question(name, block)],
833
+ }
834
+
835
+
836
+ def _decision_block(payload: dict) -> dict:
837
+ answers = payload.get("answers")
838
+ if not isinstance(answers, list) or not answers:
839
+ raise ValueError("decisions response has no answers")
840
+ named = next(
841
+ (a for a in answers if isinstance(a, dict) and a.get("name") == "answer"),
842
+ None)
843
+ block = named if named is not None else next(
844
+ (a for a in answers if isinstance(a, dict)), None)
845
+ if not isinstance(block, dict):
846
+ raise ValueError("decisions answer is not an object")
847
+ return block
848
+
849
+
850
+ def _predicate_label(probability: float, question: Optional[dict],
851
+ threshold: Optional[float]) -> str:
852
+ pos, neg = _noul_labels(question)
853
+ cut = _NOUL_THRESHOLD if threshold is None else threshold
854
+ return pos if probability >= cut else neg
855
+
856
+
857
+ class DecisionsBackend:
858
+ """POST a typed pack item to OpenAI ``/v1/decisions``.
859
+
860
+ The pack body is ``{state, questions: {answer: {type, instructions,
861
+ criteria}}}``. This translates it. ``noul`` and yes-no become a
862
+ predicate. Choice ``criteria`` become ``choices`` of ``{value,
863
+ description}``. The model is ``gpt-6-luna``.
864
+
865
+ ``KIND`` stays ``typed`` so a typed pack is not a kind mismatch. The
866
+ registry kind that selects this class is ``decisions``.
867
+
868
+ A predicate probability is P(true), mapped with the same 0.50 cut and
869
+ the same template labels as the laya noul path. A refusal is not a
870
+ label. When the response has no usage object, tokens are an estimate
871
+ of the input and ``estimated`` is true.
872
+ """
873
+
874
+ KIND = "typed"
875
+
876
+ def __init__(self, *, name: str, base_url: str, path: str,
877
+ auth: Optional[dict] = None, usage: Optional[dict] = None,
878
+ price: Optional[dict] = None, egress: str = "external",
879
+ noul_threshold: Optional[float] = None, transport=None):
880
+ self.name = name
881
+ self.base_url = base_url.rstrip("/")
882
+ self.path = path
883
+ self.auth = auth
884
+ self.usage = usage or {}
885
+ self.price = price
886
+ self.egress = egress
887
+ self.noul_threshold = noul_threshold
888
+ self.transport = transport
889
+ self.calls = 0
890
+
891
+ def missing_creds(self) -> list[str]:
892
+ if not self.auth:
893
+ return []
894
+ env = self.auth.get("env") or ""
895
+ if not os.environ.get(env):
896
+ return [f"{env} (provider {self.name})"]
897
+ return []
898
+
899
+ def secrets(self) -> list[tuple[str, str, str]]:
900
+ if not self.auth:
901
+ return []
902
+ env = self.auth.get("env") or ""
903
+ value = os.environ.get(env) or ""
904
+ if not value:
905
+ return []
906
+ return [(value, env, self.auth.get("scheme") or "")]
907
+
908
+ def generate(self, model_id: str, prompt: str, system: str = "") -> BackendResult:
909
+ t0 = time.perf_counter()
910
+ secrets = self.secrets()
911
+ try:
912
+ parsed, body = decisions_request(prompt)
913
+ input_text = body["input"]
914
+ endpoint = self.base_url + self.path
915
+ headers = {"Content-Type": "application/json"}
916
+ if self.auth:
917
+ env = self.auth["env"]
918
+ value = os.environ.get(env) or ""
919
+ scheme = self.auth.get("scheme") or ""
920
+ token = f"{scheme} {value}".strip() if scheme else value
921
+ headers[self.auth["header"]] = token
922
+ raw_body = json.dumps(body).encode()
923
+ self.calls += 1
924
+ if self.transport is not None:
925
+ status, reason, raw = self.transport(endpoint, headers, raw_body)
926
+ else:
927
+ req = urllib.request.Request(
928
+ endpoint, data=raw_body, headers=headers, method="POST")
929
+ try:
930
+ with urllib.request.urlopen(req, timeout=60) as resp:
931
+ status, reason, raw = resp.status, resp.reason, resp.read()
932
+ except urllib.error.HTTPError as exc:
933
+ status, reason, raw = exc.code, exc.reason, exc.read()
934
+ if status < 200 or status >= 300:
935
+ return BackendResult(
936
+ text="", tokens_in=0, tokens_thought=0, tokens_out=0,
937
+ latency_s=round(time.perf_counter() - t0, 6),
938
+ estimated=False,
939
+ error=_redact(f"HTTP {status} {reason}", secrets))
940
+ payload = json.loads(raw.decode() or "{}")
941
+ if not isinstance(payload, dict):
942
+ raise ValueError("decisions response is not an object")
943
+ block = _decision_block(payload)
944
+ question = _answer_block(parsed)[1]
945
+ text, p_true = _render_decision(
946
+ block, question, self.noul_threshold)
947
+ usage = payload.get("usage") if isinstance(payload.get("usage"), dict) else None
948
+ in_path = self.usage.get("in") or "usage.input_tokens"
949
+ out_path = self.usage.get("out") or "usage.output_tokens"
950
+ tin = _dig(payload, in_path) if usage is not None else None
951
+ tout = _dig(payload, out_path) if usage is not None else None
952
+ if usage is not None and tin is not None:
953
+ estimated = False
954
+ tokens_in = int(tin)
955
+ tokens_out = int(tout or 0)
956
+ else:
957
+ estimated = True
958
+ tokens_in = estimate_tokens(input_text)
959
+ tokens_out = 0
960
+ text = _redact(text, secrets)
961
+ except Exception as exc:
962
+ return BackendResult(
963
+ text="", tokens_in=0, tokens_thought=0, tokens_out=0,
964
+ latency_s=round(time.perf_counter() - t0, 6),
965
+ estimated=False, error=_redact(str(exc), secrets))
966
+ return BackendResult(
967
+ text=text,
968
+ tokens_in=tokens_in,
969
+ tokens_thought=0,
970
+ tokens_out=tokens_out,
971
+ latency_s=round(time.perf_counter() - t0, 6),
972
+ estimated=estimated,
973
+ )
974
+
975
+
976
+ def _refusal_reason(block: dict) -> str:
977
+ for key in ("reason", "message", "explanation"):
978
+ value = block.get(key)
979
+ if isinstance(value, str) and value.strip():
980
+ return value
981
+ name = block.get("name")
982
+ if isinstance(name, str) and name.strip():
983
+ return name
984
+ return "refusal"
985
+
986
+
987
+ def _render_decision(block: dict, question: dict,
988
+ threshold: Optional[float]) -> tuple[str, Optional[float]]:
989
+ """Result text, and p_true when the answer was a predicate.
990
+
991
+ A refusal has no ``answer`` key, so the runner scores it as a parse
992
+ failure. The reason stays in the text. Choice keeps the probabilities
993
+ beside a flat answer object the scorer can read.
994
+ """
995
+ if block.get("type") == "score":
996
+ return json.dumps({"refusal": "unsupported answer type: score"}), None
997
+ if block.get("type") == "refusal":
998
+ return json.dumps({"refusal": _refusal_reason(block)}), None
999
+ if block.get("type") == "predicate" or (
1000
+ block.get("type") is None and "probability" in block):
1001
+ p_true = float(block["probability"])
1002
+ label = _predicate_label(p_true, question, threshold)
1003
+ return json.dumps({"answer": label, "p_true": p_true}), p_true
1004
+ if block.get("type") == "choice" or (
1005
+ block.get("type") is None and "choice" in block):
1006
+ label = block.get("choice")
1007
+ if not isinstance(label, str) or not label:
1008
+ raise ValueError("decisions choice has no label")
1009
+ flat = json.dumps({"answer": label})
1010
+ extra = {}
1011
+ if "probabilities" in block:
1012
+ extra["probabilities"] = block["probabilities"]
1013
+ if "confidence" in block:
1014
+ extra["confidence"] = block["confidence"]
1015
+ if extra:
1016
+ return flat + "\n" + json.dumps(extra), None
1017
+ return flat, None
1018
+ raise ValueError("decisions answer is not a choice, predicate, or refusal")
@@ -52,14 +52,18 @@ def _check(name: str, entry: dict) -> dict:
52
52
  if unknown:
53
53
  raise RegistryError(f"{name}: unknown keys {sorted(unknown)}")
54
54
  kind = entry.get("kind")
55
- if kind not in {"typed", "semantic"}:
56
- raise RegistryError(f"{name}: kind must be typed or semantic")
55
+ if kind not in {"typed", "semantic", "decisions"}:
56
+ raise RegistryError(f"{name}: kind must be typed, semantic, or decisions")
57
57
  base = entry.get("base_url")
58
58
  if not isinstance(base, str) or not base.strip():
59
59
  raise RegistryError(f"{name}: base_url is required")
60
60
  path = entry.get("path")
61
61
  if path is None:
62
- path = "/v1/systemone" if kind == "typed" else "/v1/chat/completions"
62
+ path = {
63
+ "typed": "/v1/systemone",
64
+ "semantic": "/v1/chat/completions",
65
+ "decisions": "/v1/decisions",
66
+ }[kind]
63
67
  if not isinstance(path, str) or not path.startswith("/"):
64
68
  raise RegistryError(f"{name}: path must start with /")
65
69
  egress = entry.get("egress", "external")
@@ -32,6 +32,7 @@ from swarph_cli.bench.backends import (
32
32
  SubscriptionBackend,
33
33
  TypedHttpBackend,
34
34
  HttpBackend,
35
+ DecisionsBackend,
35
36
  )
36
37
  from swarph_cli.bench.pack import PackError, load_pack, slugify_theme, validate_schema
37
38
  from swarph_cli.bench.providers import RegistryError, load_registry, registry_path
@@ -221,6 +222,24 @@ def _default_packs_dir() -> Path:
221
222
  return cwd_packs
222
223
 
223
224
 
225
+ def _provider_arm(name: str, entry: dict, noul_threshold):
226
+ """The registry arm for ``provider:<name>``.
227
+
228
+ ``kind = "decisions"`` is the OpenAI Decisions translator. ``typed`` and
229
+ ``semantic`` stay on :class:`HttpBackend`, which posts a typed pack body
230
+ unchanged.
231
+ """
232
+ common = dict(
233
+ name=name, base_url=entry["base_url"], path=entry["path"],
234
+ auth=entry.get("auth"), usage=entry.get("usage"),
235
+ price=entry.get("price"), egress=entry["egress"],
236
+ noul_threshold=noul_threshold,
237
+ )
238
+ if entry["kind"] == "decisions":
239
+ return DecisionsBackend(**common)
240
+ return HttpBackend(kind=entry["kind"], **common)
241
+
242
+
224
243
  # ── dispatch ───────────────────────────────────────────────────────────────
225
244
 
226
245
  def _cmd_run(args) -> int:
@@ -249,11 +268,8 @@ def _cmd_run(args) -> int:
249
268
  entry = registry.get(spec.provider)
250
269
  if entry is None:
251
270
  continue
252
- spec.arm = HttpBackend(
253
- name=spec.provider, kind=entry["kind"], base_url=entry["base_url"],
254
- path=entry["path"], auth=entry.get("auth"), usage=entry.get("usage"),
255
- price=entry.get("price"), egress=entry["egress"],
256
- noul_threshold=pack.get("noul_threshold"))
271
+ spec.arm = _provider_arm(
272
+ spec.provider, entry, pack.get("noul_threshold"))
257
273
  allow_egress = set(args.allow_egress or [])
258
274
  runnable, warnings = preflight(
259
275
  specs, backends, pack=pack, allow_egress=allow_egress,
@@ -54,6 +54,14 @@ def _build_parser() -> argparse.ArgumentParser:
54
54
  default=None,
55
55
  help="minimum seconds between firings",
56
56
  )
57
+ create.add_argument(
58
+ "--deliver",
59
+ choices=["wake", "dm"],
60
+ default="wake",
61
+ help="delivery mode: wake (default) types into a host-local tmux pane; "
62
+ "dm schedules a deferred DM to the target peer — use dm for any "
63
+ "cell not on lab's box (card #1081)",
64
+ )
57
65
  add_common_args(create)
58
66
 
59
67
  ls = sub.add_parser("list", help="list scheduled events")
@@ -188,6 +196,8 @@ def _run_create(args: argparse.Namespace) -> int:
188
196
  body["out_channel"] = args.out_channel
189
197
  if args.min_interval is not None:
190
198
  body["min_interval_sec"] = args.min_interval
199
+ if args.deliver == "dm":
200
+ body["deliver"] = "dm"
191
201
  status, payload = post_json(f"{base}/scheduled-events", body, token)
192
202
  if not _ok(status):
193
203
  return _fail("create", status, payload)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.16
3
+ Version: 0.72.17
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
@@ -219,6 +219,7 @@ tests/test_1073_triage.py
219
219
  tests/test_1073_triage_board.py
220
220
  tests/test_1073_wheel_install.py
221
221
  tests/test_1075_task_cli.py
222
+ tests/test_1083_openai_decisions.py
222
223
  tests/test_124_register_merge.py
223
224
  tests/test_126_pending_from_complete.py
224
225
  tests/test_132_ensure_monitor_state_dir.py