swarph-cli 0.72.16__tar.gz → 0.72.18__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 (509) hide show
  1. {swarph_cli-0.72.16/src/swarph_cli.egg-info → swarph_cli-0.72.18}/PKG-INFO +9 -1
  2. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/README.md +8 -0
  3. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/pyproject.toml +1 -1
  4. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/backends.py +341 -0
  6. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/providers.py +7 -3
  7. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/bench.py +21 -5
  8. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/board.py +117 -17
  9. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/mesh.py +70 -0
  10. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/roster.py +196 -0
  11. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/schedule.py +10 -0
  12. swarph_cli-0.72.18/src/swarph_cli/quota_sync.py +252 -0
  13. {swarph_cli-0.72.16 → swarph_cli-0.72.18/src/swarph_cli.egg-info}/PKG-INFO +9 -1
  14. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/SOURCES.txt +5 -0
  15. swarph_cli-0.72.18/tests/test_1068_structured_accept_cli.py +104 -0
  16. swarph_cli-0.72.18/tests/test_1071_quota_sync.py +307 -0
  17. swarph_cli-0.72.18/tests/test_1083_openai_decisions.py +376 -0
  18. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_323_mistral_backend.py +12 -1
  19. swarph_cli-0.72.18/tests/test_copilot_pane_reader.py +165 -0
  20. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_schedule_command.py +31 -0
  21. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/LICENSE +0 -0
  22. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/setup.cfg +0 -0
  23. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/__main__.py +0 -0
  24. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/__init__.py +0 -0
  25. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/claude_cli.py +0 -0
  26. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/codex_cli.py +0 -0
  27. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/pack.py +0 -0
  28. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/prices.py +0 -0
  29. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/quality.py +0 -0
  30. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/refresh_prices.py +0 -0
  31. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/rules.py +0 -0
  32. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/runner.py +0 -0
  33. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/signal_score.py +0 -0
  34. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/validate.py +0 -0
  35. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/caller.py +0 -0
  36. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/__init__.py +0 -0
  37. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/harden.py +0 -0
  38. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/lineage.py +0 -0
  39. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/liveness.py +0 -0
  40. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/manifest.py +0 -0
  41. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/paths.py +0 -0
  42. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/verify.py +0 -0
  43. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/cell.py +0 -0
  44. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/chain_token.py +0 -0
  45. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/channel.py +0 -0
  46. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/__init__.py +0 -0
  47. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_content.py +0 -0
  48. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_display.py +0 -0
  49. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_gateway_client.py +0 -0
  50. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/add.py +0 -0
  51. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/board_merge_check.py +0 -0
  52. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/brain.py +0 -0
  53. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/brain_ask.py +0 -0
  54. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell.py +0 -0
  55. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell_probe.py +0 -0
  56. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  57. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/channel.py +0 -0
  58. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/chat.py +0 -0
  59. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codegraph.py +0 -0
  60. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  61. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  62. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codex_waker.py +0 -0
  63. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/compress.py +0 -0
  64. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/daemon.py +0 -0
  65. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/dreaming.py +0 -0
  66. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/event.py +0 -0
  67. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/gateway.py +0 -0
  68. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/gh_route.py +0 -0
  69. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/group.py +0 -0
  70. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/guide.py +0 -0
  71. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/highlight.py +0 -0
  72. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  73. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hook_output.py +0 -0
  74. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hooks.py +0 -0
  75. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/import_session.py +0 -0
  76. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/init.py +0 -0
  77. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  78. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_hook.py +0 -0
  79. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  80. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  81. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  82. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  83. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/lane.py +0 -0
  84. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/mcp_server.py +0 -0
  85. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory.py +0 -0
  86. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  87. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory_sync.py +0 -0
  88. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/monitor.py +0 -0
  89. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/okf_links.py +0 -0
  90. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/onboard.py +0 -0
  91. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  92. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  93. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/protocol_handler.py +0 -0
  94. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/ratify.py +0 -0
  95. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  96. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/security.py +0 -0
  97. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/service.py +0 -0
  98. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/spawn.py +0 -0
  99. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/sweep.py +0 -0
  100. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/timeline.py +0 -0
  101. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/triage.py +0 -0
  102. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/version.py +0 -0
  103. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  104. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/watchdog.py +0 -0
  105. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/__init__.py +0 -0
  106. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/levers.py +0 -0
  107. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/marker.py +0 -0
  108. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/verify.py +0 -0
  109. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/console_safe.py +0 -0
  110. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/delivery_queue.py +0 -0
  111. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dm_frame.py +0 -0
  112. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/__init__.py +0 -0
  113. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/candidates.py +0 -0
  114. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/clone.py +0 -0
  115. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/enrich.py +0 -0
  116. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/index_check.py +0 -0
  117. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/notify.py +0 -0
  118. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/organize.py +0 -0
  119. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/probes.py +0 -0
  120. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/report.py +0 -0
  121. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/run.py +0 -0
  122. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/slm.py +0 -0
  123. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/transcripts.py +0 -0
  124. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/verify.py +0 -0
  125. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/__init__.py +0 -0
  126. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/feature_registry.py +0 -0
  127. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/lanes_control.py +0 -0
  128. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/schema.sql +0 -0
  129. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/server.py +0 -0
  130. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/services_control.py +0 -0
  131. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway_default.py +0 -0
  132. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gh_identity.py +0 -0
  133. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/guide/GUIDE.md +0 -0
  134. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/identity.py +0 -0
  135. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/main.py +0 -0
  136. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/multiplexer.py +0 -0
  137. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/__init__.py +0 -0
  138. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/__main__.py +0 -0
  139. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/api.py +0 -0
  140. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/cli.py +0 -0
  141. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/config.py +0 -0
  142. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/fmt.py +0 -0
  143. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/orphan_daemons.py +0 -0
  144. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/pane_probe.py +0 -0
  145. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/parsers/__init__.py +0 -0
  146. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/parsers/claude.py +0 -0
  147. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  148. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  149. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  150. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  151. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  152. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  153. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  154. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  155. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  156. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  157. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  158. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  159. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_executor.py +0 -0
  160. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_executor_windows.py +0 -0
  161. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_reconciliation.py +0 -0
  162. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_reply_outbox.py +0 -0
  163. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_host.py +0 -0
  164. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_payload.py +0 -0
  165. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_reply.py +0 -0
  166. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_staging.py +0 -0
  167. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/query_filters.py +0 -0
  168. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/rowclear.py +0 -0
  169. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/__init__.py +0 -0
  170. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  171. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  172. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  173. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  174. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  175. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scrub.py +0 -0
  176. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/__init__.py +0 -0
  177. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/app.py +0 -0
  178. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/providers.py +0 -0
  179. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/session_bridge.py +0 -0
  180. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  181. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  182. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/source_text.py +0 -0
  183. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/stall_alert.py +0 -0
  184. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/codex-waker.default +0 -0
  185. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  186. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  187. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  188. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  189. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  190. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  191. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  192. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  193. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  194. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  195. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  196. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  197. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  198. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  199. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  200. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  201. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  202. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/timeline_paths.py +0 -0
  203. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/tokens.py +0 -0
  204. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/__init__.py +0 -0
  205. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/backend.py +0 -0
  206. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/job.py +0 -0
  207. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/owners.json +0 -0
  208. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/router.py +0 -0
  209. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/rows.py +0 -0
  210. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/sensor.py +0 -0
  211. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  212. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  213. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/requires.txt +0 -0
  214. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/top_level.txt +0 -0
  215. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1006_board_publisher.py +0 -0
  216. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1006_session_board.py +0 -0
  217. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1010_strand_only.py +0 -0
  218. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1013_spawn_channel.py +0 -0
  219. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1017_muse_tui.py +0 -0
  220. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1050_surrogate_wake.py +0 -0
  221. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1052_opencode_deaf.py +0 -0
  222. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1059_watchdog_unread_env.py +0 -0
  223. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1061_roster_cli.py +0 -0
  224. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1064_stuck_sweep.py +0 -0
  225. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1065_release.py +0 -0
  226. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1066_quiet_wakes.py +0 -0
  227. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_triage.py +0 -0
  228. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_triage_board.py +0 -0
  229. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_wheel_install.py +0 -0
  230. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1075_task_cli.py +0 -0
  231. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_124_register_merge.py +0 -0
  232. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_126_pending_from_complete.py +0 -0
  233. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  234. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_138_memory_navigate.py +0 -0
  235. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_139_tool_probe.py +0 -0
  236. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_183_card_anchor.py +0 -0
  237. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_184_notify_honesty.py +0 -0
  238. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_201_win32_bash_refusal.py +0 -0
  239. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_243_onboard_per_peer_token.py +0 -0
  240. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_307_task4_ask_reply.py +0 -0
  241. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  242. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_319_refusal_exit_codes.py +0 -0
  243. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_332_report_organize_findings.py +0 -0
  244. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_356_query_filters.py +0 -0
  245. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_397_gh_identity_router.py +0 -0
  246. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_397_router_preflight.py +0 -0
  247. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_402_one_identity_resolver.py +0 -0
  248. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_432_rowclear.py +0 -0
  249. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_467b_onboard_operator_credential.py +0 -0
  250. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_472_register_caller_binding.py +0 -0
  251. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_484_codex_queue_sink.py +0 -0
  252. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_523_guide.py +0 -0
  253. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_524_conftest_hermeticity.py +0 -0
  254. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_532_accept_flag.py +0 -0
  255. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_535_version_capability.py +0 -0
  256. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_538_signal_bench.py +0 -0
  257. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_546_default_gateway.py +0 -0
  258. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_547_verbs_from_registry.py +0 -0
  259. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_548_gateway_remainder.py +0 -0
  260. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_552_codegraph_gateway.py +0 -0
  261. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_555_relay_row_shape.py +0 -0
  262. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_556_relay_freshness.py +0 -0
  263. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_559_edges_truncated.py +0 -0
  264. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_564_bootstrap_first_peer.py +0 -0
  265. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  266. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_579_register_bootstrap_message.py +0 -0
  267. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_591_cli_step_graph.py +0 -0
  268. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_644_monitor_supervision.py +0 -0
  269. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_648_install_unit.py +0 -0
  270. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_654_reexec.py +0 -0
  271. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_656_owned_slm.py +0 -0
  272. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_684_dreaming_verb.py +0 -0
  273. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_689_pkg_version_order.py +0 -0
  274. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_715_grok_wake.py +0 -0
  275. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_716_timeline_one_path.py +0 -0
  276. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_723_grok_0251.py +0 -0
  277. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  278. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_725_stdio_reconfigure.py +0 -0
  279. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_726_grok_limit_screen.py +0 -0
  280. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_arm_check_delivery_gap.py +0 -0
  281. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_dm_once.py +0 -0
  282. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_wake_watchdog.py +0 -0
  283. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  284. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_735_dm_mcp_tools.py +0 -0
  285. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_739_wake_writer.py +0 -0
  286. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_740_edit_project.py +0 -0
  287. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_762_cards_edit_priority.py +0 -0
  288. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_764_enrich_collapse.py +0 -0
  289. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_801_confirm_flow_cli.py +0 -0
  290. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_802_stage_history.py +0 -0
  291. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_codegraph_hook_noise.py +0 -0
  292. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_install_posttooluse_only.py +0 -0
  293. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  294. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_829_counterfactual_column.py +0 -0
  295. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_830_hook_binding_version.py +0 -0
  296. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_84_cli_labels.py +0 -0
  297. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_866_doctrine_guide.py +0 -0
  298. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_872_identity_refuses_not_warns.py +0 -0
  299. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_874_source_text.py +0 -0
  300. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  301. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_937_dreaming_notify.py +0 -0
  302. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_938_organize_line_limit.py +0 -0
  303. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_941_bench_backends.py +0 -0
  304. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_941_harness_342.py +0 -0
  305. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_945_same_line_pairing.py +0 -0
  306. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_955_prompt_audit.py +0 -0
  307. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_957_auth_http.py +0 -0
  308. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_959_venv_interpreter.py +0 -0
  309. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_960_channel.py +0 -0
  310. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  311. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_961_cursor_grok_footer.py +0 -0
  312. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_961_loud_stop.py +0 -0
  313. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_963_plugin_manifest.py +0 -0
  314. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_964_channel_cell.py +0 -0
  315. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_965_mesh_wait.py +0 -0
  316. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_975_channel_hop.py +0 -0
  317. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_add_security_gate.py +0 -0
  318. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_add.py +0 -0
  319. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_hash.py +0 -0
  320. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_lib.py +0 -0
  321. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_mcp.py +0 -0
  322. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_skill.py +0 -0
  323. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_tool.py +0 -0
  324. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_uri.py +0 -0
  325. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_backends.py +0 -0
  326. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_claude_cli.py +0 -0
  327. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_codex_cli.py +0 -0
  328. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_command.py +0 -0
  329. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_pack.py +0 -0
  330. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_prices.py +0 -0
  331. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_quality.py +0 -0
  332. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_runner.py +0 -0
  333. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_validate.py +0 -0
  334. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_card_thread.py +0 -0
  335. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_command.py +0 -0
  336. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_merge_check.py +0 -0
  337. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_obligation_close.py +0 -0
  338. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_command.py +0 -0
  339. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_env_diagnosis.py +0 -0
  340. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_identity_explicitness.py +0 -0
  341. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_command.py +0 -0
  342. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_caller_meta_guard.py +0 -0
  343. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_lineage.py +0 -0
  344. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_liveness.py +0 -0
  345. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_manifest.py +0 -0
  346. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_paths.py +0 -0
  347. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_security.py +0 -0
  348. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_command_dispatch.py +0 -0
  349. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_harden.py +0 -0
  350. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_loader.py +0 -0
  351. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_selfcheck.py +0 -0
  352. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_verify.py +0 -0
  353. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_chain_token.py +0 -0
  354. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_command.py +0 -0
  355. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  356. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_post.py +0 -0
  357. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_wake_policy_filter.py +0 -0
  358. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_chat_command.py +0 -0
  359. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_claude_parser.py +0 -0
  360. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_claude_tmux_template.py +0 -0
  361. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_caller_scope.py +0 -0
  362. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_command.py +0 -0
  363. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_hook.py +0 -0
  364. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_mcp.py +0 -0
  365. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codex_waker.py +0 -0
  366. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_caller_convention.py +0 -0
  367. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_command.py +0 -0
  368. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_levers.py +0 -0
  369. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_marker.py +0 -0
  370. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_verify.py +0 -0
  371. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_console_encoding_e2e.py +0 -0
  372. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_content_composition.py +0 -0
  373. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_copilot_membrane.py +0 -0
  374. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cursor_membrane.py +0 -0
  375. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_command.py +0 -0
  376. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_console_encoding.py +0 -0
  377. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_delivery.py +0 -0
  378. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_delivery_queue.py +0 -0
  379. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_display_sanitize.py +0 -0
  380. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  381. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_event_emit.py +0 -0
  382. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_feature_to_uri.py +0 -0
  383. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_brain.py +0 -0
  384. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_client.py +0 -0
  385. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_command.py +0 -0
  386. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_message_idempotency.py +0 -0
  387. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  388. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_group_command.py +0 -0
  389. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_guide_commands_parse.py +0 -0
  390. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_guide_selfcheck.py +0 -0
  391. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_heartbeat_check.py +0 -0
  392. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_highlight_command.py +0 -0
  393. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hook_output.py +0 -0
  394. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_activity_marker.py +0 -0
  395. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_add.py +0 -0
  396. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_bundle.py +0 -0
  397. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_init.py +0 -0
  398. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_legacy_generation_removal.py +0 -0
  399. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_lifecycle.py +0 -0
  400. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_merge.py +0 -0
  401. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_verify.py +0 -0
  402. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  403. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_win32_command_path.py +0 -0
  404. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_import_command.py +0 -0
  405. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_inbox_identity_provenance.py +0 -0
  406. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_init_command.py +0 -0
  407. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_init_provider_menu.py +0 -0
  408. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_codex_hooks.py +0 -0
  409. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_hook.py +0 -0
  410. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_multiplexer_command.py +0 -0
  411. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_postcompact_hook.py +0 -0
  412. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_wake_hook.py +0 -0
  413. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_lane_command.py +0 -0
  414. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_main.py +0 -0
  415. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_main_unknown_verb_guard.py +0 -0
  416. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mcp_missing_hint.py +0 -0
  417. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mcp_server.py +0 -0
  418. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_command.py +0 -0
  419. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_emit_snippet.py +0 -0
  420. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_gateway.py +0 -0
  421. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_sync.py +0 -0
  422. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_sync_membrane.py +0 -0
  423. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_command.py +0 -0
  424. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_inbox_mark_read.py +0 -0
  425. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_inbox_sanitize.py +0 -0
  426. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_originate_thread_623.py +0 -0
  427. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_send_unknown_peer.py +0 -0
  428. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar.py +0 -0
  429. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  430. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  431. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  432. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_meta_edge_identity.py +0 -0
  433. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_channel_poll.py +0 -0
  434. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_cli.py +0 -0
  435. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_daemon_collision.py +0 -0
  436. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_self_name_verification.py +0 -0
  437. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_state_split.py +0 -0
  438. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_multiplexer.py +0 -0
  439. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_muse_membrane.py +0 -0
  440. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_muse_own_cli.py +0 -0
  441. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_obligation_close_is_surfaced.py +0 -0
  442. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_okf_links.py +0 -0
  443. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_onboard_command.py +0 -0
  444. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_opencode_membrane.py +0 -0
  445. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_opencode_plugin.py +0 -0
  446. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_operator_me.py +0 -0
  447. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_orphan_daemons.py +0 -0
  448. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_packaged_artifacts_exist.py +0 -0
  449. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_packaged_skills.py +0 -0
  450. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_executor.py +0 -0
  451. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_executor_windows.py +0 -0
  452. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_drain_command.py +0 -0
  453. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_drain_supervisor.py +0 -0
  454. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_outbox.py +0 -0
  455. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_transport.py +0 -0
  456. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_e2e.py +0 -0
  457. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_host.py +0 -0
  458. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_payload.py +0 -0
  459. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_reply.py +0 -0
  460. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_postcompact_recall.py +0 -0
  461. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_process_liveness.py +0 -0
  462. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_protocol_handler.py +0 -0
  463. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_providers_isolation.py +0 -0
  464. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_ratify_command.py +0 -0
  465. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_readme_claims_no_gateway_default.py +0 -0
  466. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_repl_caller_convention.py +0 -0
  467. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_schedule_create_contract.py +0 -0
  468. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_scrub.py +0 -0
  469. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_security.py +0 -0
  470. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_app.py +0 -0
  471. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_command.py +0 -0
  472. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_providers.py +0 -0
  473. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_inject.py +0 -0
  474. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_probe.py +0 -0
  475. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_resolve.py +0 -0
  476. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_path_role_gate.py +0 -0
  477. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_shipped_units_match_the_cli.py +0 -0
  478. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_chat.py +0 -0
  479. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_one_shot.py +0 -0
  480. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_phase_5_5.py +0 -0
  481. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_base_hoist.py +0 -0
  482. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_command.py +0 -0
  483. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_identity_stamp.py +0 -0
  484. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_live_pin.py +0 -0
  485. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_mitosis_lineage.py +0 -0
  486. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_tmux_session.py +0 -0
  487. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_windows_relaunch.py +0 -0
  488. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_stall_alert.py +0 -0
  489. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_subprocess_decode_encoding.py +0 -0
  490. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_timeline_command.py +0 -0
  491. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_runstate_defer.py +0 -0
  492. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_session_reanchor.py +0 -0
  493. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_stale.py +0 -0
  494. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_submit_verify.py +0 -0
  495. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_token_file_one_parser.py +0 -0
  496. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_token_precedence_explicit_first.py +0 -0
  497. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_unified_token_resolver.py +0 -0
  498. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_version_command.py +0 -0
  499. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_version_consistency.py +0 -0
  500. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_vibe_membrane.py +0 -0
  501. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog.py +0 -0
  502. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake.py +0 -0
  503. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  504. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  505. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_emit_health.py +0 -0
  506. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_install_flags.py +0 -0
  507. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_model_rung.py +0 -0
  508. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_stale_peers.py +0 -0
  509. {swarph_cli-0.72.16 → swarph_cli-0.72.18}/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.18
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
@@ -497,6 +497,14 @@ swarph monitor stop
497
497
  | `none` | genuinely nothing: observe, append `inbox.log`, no ledger, **no unread tracking**. |
498
498
  | `webhook:<url>` | **held** pending an egress review — exits non-zero rather than silently no-op. |
499
499
 
500
+ **Copilot tmux panes.** The reader recognizes Copilot CLI 1.0.94's color input
501
+ box and `--no-color` composer using the bottom AIC summary, input borders, and
502
+ status footer. It defers while `esc interrupt` indicates a running turn or
503
+ the composer holds a human draft. The wake names `swarph_dm_unread`; delivery
504
+ is recorded only after the composer is observed clear. Missing or incomplete
505
+ pane captures send no keystrokes and leave delivery pending with a visible
506
+ failure. Keep `pull` for layouts that hide or customize this chrome.
507
+
500
508
  **Two pieces of state that never share a variable.** The *observation cursor* (`cursor.json`) is what this monitor has READ from the gateway; it advances on observation, always, gated on nothing. A *delivery ledger* (`ledgers.json`, one per sink) advances only when that sink is satisfied and may lag arbitrarily far. So a dead tmux pane can no longer freeze the cursor, one dead sink cannot stall another, and a sink attached tomorrow starts with an empty ledger and replays from `inbox.log` (bounded, and it reports what it skipped).
501
509
 
502
510
  **Pull beats push.** Every push sink's liveness is a precondition for hearing anything — if the pane dies, "no wake arrived" is indistinguishable from "no mail arrived". A pull check run *by* the cell lives one layer above tmux and cannot die with it. Designed to drop straight into a SessionStart hook:
@@ -448,6 +448,14 @@ swarph monitor stop
448
448
  | `none` | genuinely nothing: observe, append `inbox.log`, no ledger, **no unread tracking**. |
449
449
  | `webhook:<url>` | **held** pending an egress review — exits non-zero rather than silently no-op. |
450
450
 
451
+ **Copilot tmux panes.** The reader recognizes Copilot CLI 1.0.94's color input
452
+ box and `--no-color` composer using the bottom AIC summary, input borders, and
453
+ status footer. It defers while `esc interrupt` indicates a running turn or
454
+ the composer holds a human draft. The wake names `swarph_dm_unread`; delivery
455
+ is recorded only after the composer is observed clear. Missing or incomplete
456
+ pane captures send no keystrokes and leave delivery pending with a visible
457
+ failure. Keep `pull` for layouts that hide or customize this chrome.
458
+
451
459
  **Two pieces of state that never share a variable.** The *observation cursor* (`cursor.json`) is what this monitor has READ from the gateway; it advances on observation, always, gated on nothing. A *delivery ledger* (`ledgers.json`, one per sink) advances only when that sink is satisfied and may lag arbitrarily far. So a dead tmux pane can no longer freeze the cursor, one dead sink cannot stall another, and a sink attached tomorrow starts with an empty ledger and replays from `inbox.log` (bounded, and it reports what it skipped).
452
460
 
453
461
  **Pull beats push.** Every push sink's liveness is a precondition for hearing anything — if the pane dies, "no wake arrived" is indistinguishable from "no mail arrived". A pull check run *by* the cell lives one layer above tmux and cannot die with it. Designed to drop straight into a SessionStart hook:
@@ -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.18"
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.18"
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,