agent-switchboard 2.2.2__tar.gz → 2.3.0__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 (270) hide show
  1. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/ARCS.md +6 -2
  2. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/PKG-INFO +32 -1
  3. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/README.md +31 -0
  4. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/claude-code.md +102 -20
  5. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/codex-cli.md +19 -13
  6. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/concepts.md +16 -0
  7. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/quickstart.md +2 -2
  8. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/session-capsule/README.md +18 -0
  9. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/pyproject.toml +1 -1
  10. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/ROADMAP.md +306 -0
  11. agent_switchboard-2.3.0/roadmap/items/a-drain-that-retries-forever-never-escalates.yaml +70 -0
  12. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/cross-key-rendezvous.yaml +16 -0
  13. agent_switchboard-2.3.0/roadmap/items/discovery-is-uneven-and-delivery-is-unknowable.yaml +80 -0
  14. agent_switchboard-2.3.0/roadmap/items/first-contact-needs-a-key-it-cannot-have.yaml +61 -0
  15. agent_switchboard-2.3.0/roadmap/items/inbox-consumes-what-it-cannot-open.yaml +50 -0
  16. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/__init__.py +1 -1
  17. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/claude_session.py +57 -6
  18. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/cli.py +250 -21
  19. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/desktop.py +8 -3
  20. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/mcp_server.py +51 -2
  21. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/rendezvous.py +44 -0
  22. agent_switchboard-2.3.0/src/switchboard/skill/switchboard-coordinate/SKILL.md +332 -0
  23. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/conftest.py +11 -0
  24. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_claude_session.py +65 -7
  25. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_init.py +76 -2
  26. agent_switchboard-2.3.0/tests/test_sending_opens_a_conversation.py +301 -0
  27. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.dockerignore +0 -0
  28. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/ci.yml +0 -0
  29. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/deploy.yml +0 -0
  30. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/pages.yml +0 -0
  31. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/publish-viewer.yml +0 -0
  32. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/publish.yml +0 -0
  33. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.github/workflows/roadmap.yml +0 -0
  34. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/.gitignore +0 -0
  35. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/AGENTS.md +0 -0
  36. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/CONTRIBUTING.md +0 -0
  37. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/Dockerfile +0 -0
  38. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/LICENSE +0 -0
  39. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/demo/README.md +0 -0
  40. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/demo/chess.sh +0 -0
  41. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/demo/run.sh +0 -0
  42. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docker-compose.yml +0 -0
  43. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/adaptive-timing.md +0 -0
  44. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/api.md +0 -0
  45. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/deployment.md +0 -0
  46. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/drills.md +0 -0
  47. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/encryption.md +0 -0
  48. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/environments.md +0 -0
  49. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/images/viewer.png +0 -0
  50. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/layers.md +0 -0
  51. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/managed-hub.md +0 -0
  52. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/model.md +0 -0
  53. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/seam.md +0 -0
  54. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/testing.md +0 -0
  55. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/upgrading.md +0 -0
  56. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/viewer.md +0 -0
  57. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/docs/why-this-exists.md +0 -0
  58. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/examples/coordinated_worker.py +0 -0
  59. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/session-capsule/cc.sh +0 -0
  60. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/session-capsule/claude_session_capsule.py +0 -0
  61. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/README.md +0 -0
  62. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/pyproject.toml +0 -0
  63. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/__init__.py +0 -0
  64. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/__main__.py +0 -0
  65. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/viewer.py +0 -0
  66. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/web/index.html +0 -0
  67. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/web/render.js +0 -0
  68. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/web/switchboard-open.js +0 -0
  69. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/extras/viewer/switchboard_viewer/web/switchboard-room.js +0 -0
  70. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/README.md +0 -0
  71. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/arcs/distribution.yaml +0 -0
  72. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/arcs/hub-boundary.yaml +0 -0
  73. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/arcs/setup-and-first-run.yaml +0 -0
  74. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/arcs/ttl-ceilings.yaml +0 -0
  75. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/a-lobby-derived-from-the-key.yaml +0 -0
  76. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/abuse-control-after-authorization.yaml +0 -0
  77. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/automatic-session-checkpoints.yaml +0 -0
  78. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/board-ttl-ceiling.yaml +0 -0
  79. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/ci-workspace-is-public.yaml +0 -0
  80. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/clients-that-cannot-post.yaml +0 -0
  81. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/connect-failure-message.yaml +0 -0
  82. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/every-silent-failure-looks-like-a-quiet-room.yaml +0 -0
  83. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/hooks-warning-false-positive.yaml +0 -0
  84. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/hub-origin-reachable-bypassing-the-edge.yaml +0 -0
  85. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/identity-rebinds-on-branch-change.yaml +0 -0
  86. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/init-writes-rooms-file.yaml +0 -0
  87. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/intermittent-suite-failure.yaml +0 -0
  88. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/joining-agent-sees-empty-inbox.yaml +0 -0
  89. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/known-rooms-address-book.yaml +0 -0
  90. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/one-resolved-context-across-surfaces.yaml +0 -0
  91. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/presence-ttl-is-not-one-size.yaml +0 -0
  92. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/provisioned-token-is-stale-and-nothing-says-so.yaml +0 -0
  93. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/publish-hub-container-image.yaml +0 -0
  94. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/read-only-rooms.yaml +0 -0
  95. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/robots-policy-for-public-hosts.yaml +0 -0
  96. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/roles-and-authority-between-agents.yaml +0 -0
  97. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/seal-agent-meta.yaml +0 -0
  98. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/selective-wake-for-the-listener.yaml +0 -0
  99. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/stale-resolver-references.yaml +0 -0
  100. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/stale-token-in-session-env.yaml +0 -0
  101. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/standing-checks-that-nothing-runs.yaml +0 -0
  102. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/timing-cold-start-in-ephemeral-environments.yaml +0 -0
  103. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/ttl-clamped-silently.yaml +0 -0
  104. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/unread-dms-not-shown-outside-mcp.yaml +0 -0
  105. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/roadmap/items/write-parity-across-surfaces.yaml +0 -0
  106. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/scripts/deploy.sh +0 -0
  107. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/auth.py +0 -0
  108. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/client.py +0 -0
  109. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/config.py +0 -0
  110. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/crypto.py +0 -0
  111. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/drill.py +0 -0
  112. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/guidance.py +0 -0
  113. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/handoff.py +0 -0
  114. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/holds.py +0 -0
  115. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/invite.py +0 -0
  116. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/knownrooms.py +0 -0
  117. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/load.py +0 -0
  118. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/notify.py +0 -0
  119. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/peers.py +0 -0
  120. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/rooms.py +0 -0
  121. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/scripts/wake-on-message.sh +0 -0
  122. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/server.py +0 -0
  123. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/signing.py +0 -0
  124. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/001-SKILL.md +0 -0
  125. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/002-SKILL.md +0 -0
  126. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/003-SKILL.md +0 -0
  127. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/004-SKILL.md +0 -0
  128. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/005-SKILL.md +0 -0
  129. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/006-SKILL.md +0 -0
  130. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/007-SKILL.md +0 -0
  131. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/008-SKILL.md +0 -0
  132. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/009-SKILL.md +0 -0
  133. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/010-SKILL.md +0 -0
  134. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/011-SKILL.md +0 -0
  135. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/012-SKILL.md +0 -0
  136. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/013-SKILL.md +0 -0
  137. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/014-SKILL.md +0 -0
  138. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/015-SKILL.md +0 -0
  139. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/016-SKILL.md +0 -0
  140. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/017-SKILL.md +0 -0
  141. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/018-SKILL.md +0 -0
  142. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/019-SKILL.md +0 -0
  143. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/020-SKILL.md +0 -0
  144. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/021-SKILL.md +0 -0
  145. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/022-SKILL.md +0 -0
  146. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/023-SKILL.md +0 -0
  147. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/024-SKILL.md +0 -0
  148. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/skill/history/025-SKILL.md +0 -0
  149. /agent_switchboard-2.2.2/src/switchboard/skill/switchboard-coordinate/SKILL.md → /agent_switchboard-2.3.0/src/switchboard/skill/history/026-SKILL.md +0 -0
  150. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/spec.py +0 -0
  151. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/store.py +0 -0
  152. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/testing.py +0 -0
  153. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/timing.py +0 -0
  154. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/src/switchboard/writekey.py +0 -0
  155. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/README.md +0 -0
  156. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/__init__.py +0 -0
  157. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/analysis.py +0 -0
  158. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/economy.py +0 -0
  159. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/flow.py +0 -0
  160. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/llm.py +0 -0
  161. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/manager.py +0 -0
  162. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/report.py +0 -0
  163. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/run.py +0 -0
  164. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier1.json +0 -0
  165. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier1_labour.json +0 -0
  166. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier1_rounds.json +0 -0
  167. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_bound.json +0 -0
  168. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_built.json +0 -0
  169. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_free.json +0 -0
  170. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_paid.json +0 -0
  171. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_spend.json +0 -0
  172. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed1_told.json +0 -0
  173. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/tier2_seed41_bound.json +0 -0
  174. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter/traders.py +0 -0
  175. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter_experiment.py +0 -0
  176. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/barter_llm_experiment.py +0 -0
  177. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_harness.py +0 -0
  178. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/RESULTS.md +0 -0
  179. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_A_s31337.txt +0 -0
  180. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_A_s4242.txt +0 -0
  181. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_A_s7777.txt +0 -0
  182. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_B_s31337.txt +0 -0
  183. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_B_s4242.txt +0 -0
  184. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_B_s7777.txt +0 -0
  185. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_C_s31337.txt +0 -0
  186. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_C_s4242.txt +0 -0
  187. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/prompt_C_s7777.txt +0 -0
  188. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_A_s31337.json +0 -0
  189. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_A_s4242.json +0 -0
  190. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_A_s7777.json +0 -0
  191. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_B_s31337.json +0 -0
  192. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_B_s4242.json +0 -0
  193. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_B_s7777.json +0 -0
  194. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_C_s31337.json +0 -0
  195. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_C_s4242.json +0 -0
  196. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_haiku_C_s7777.json +0 -0
  197. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_A_s31337.json +0 -0
  198. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_A_s4242.json +0 -0
  199. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_A_s7777.json +0 -0
  200. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_B_s31337.json +0 -0
  201. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_B_s4242.json +0 -0
  202. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_B_s7777.json +0 -0
  203. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_C_s31337.json +0 -0
  204. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_C_s4242.json +0 -0
  205. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_opus_C_s7777.json +0 -0
  206. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_A_s31337.json +0 -0
  207. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_A_s4242.json +0 -0
  208. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_A_s7777.json +0 -0
  209. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_B_s31337.json +0 -0
  210. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_B_s4242.json +0 -0
  211. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_B_s7777.json +0 -0
  212. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_C_s31337.json +0 -0
  213. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_C_s4242.json +0 -0
  214. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_pilot/response_sonnet_C_s7777.json +0 -0
  215. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/tier2_run.py +0 -0
  216. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/experiments/timing_experiment.py +0 -0
  217. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_api.py +0 -0
  218. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_arrive.py +0 -0
  219. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_auth.py +0 -0
  220. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_barter.py +0 -0
  221. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_barter_llm.py +0 -0
  222. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_board.py +0 -0
  223. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_checkin.py +0 -0
  224. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_config.py +0 -0
  225. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_global_flags.py +0 -0
  226. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_serve.py +0 -0
  227. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_session.py +0 -0
  228. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cli_timing.py +0 -0
  229. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_client_invite.py +0 -0
  230. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_config.py +0 -0
  231. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_cors_write_headers.py +0 -0
  232. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_crypto.py +0 -0
  233. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_dash_ids.py +0 -0
  234. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_declare.py +0 -0
  235. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_deployment.py +0 -0
  236. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_desktop.py +0 -0
  237. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_drill.py +0 -0
  238. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_evening_replayed.py +0 -0
  239. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_example_viewer.py +0 -0
  240. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_failure_messages.py +0 -0
  241. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_handoff.py +0 -0
  242. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_help.py +0 -0
  243. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_invite.py +0 -0
  244. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_isolated_hub.py +0 -0
  245. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_known_rooms.py +0 -0
  246. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_leave_a_ghost.py +0 -0
  247. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_load.py +0 -0
  248. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_lobby.py +0 -0
  249. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_mcp.py +0 -0
  250. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_mcp_session.py +0 -0
  251. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_notify.py +0 -0
  252. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_rendezvous.py +0 -0
  253. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_rooms.py +0 -0
  254. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_spec.py +0 -0
  255. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_store.py +0 -0
  256. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_surface_parity.py +0 -0
  257. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_testing.py +0 -0
  258. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_tier2_harness.py +0 -0
  259. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_timing.py +0 -0
  260. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_timing_experiment.py +0 -0
  261. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_transient_failures.py +0 -0
  262. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_version.py +0 -0
  263. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_wake_listener.py +0 -0
  264. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_web_invite.py +0 -0
  265. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_web_page.py +0 -0
  266. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_web_reader.py +0 -0
  267. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_web_snapshot.py +0 -0
  268. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_whisper.py +0 -0
  269. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_workflow_token.py +0 -0
  270. {agent_switchboard-2.2.2 → agent_switchboard-2.3.0}/tests/test_write_keys.py +0 -0
@@ -41,7 +41,7 @@ issue — read off a line in the README rather than reported by anyone.
41
41
 
42
42
  ### 🟠 What protects a room once the hub stops authorizing anyone
43
43
 
44
- `hub-boundary` · 9 item(s), 6 startable
44
+ `hub-boundary` · 10 item(s), 7 startable
45
45
 
46
46
  `https://github.com/gald33/switchboard/issues/61` · `https://github.com/gald33/switchboard/issues/73` · `docs/model.md` · `docs/encryption.md`
47
47
 
@@ -67,6 +67,7 @@ under pressure.
67
67
 
68
68
  | item | status | priority |
69
69
  |---|---|---|
70
+ | `a-drain-that-retries-forever-never-escalates` | ready | now |
70
71
  | `ci-workspace-is-public` | ready | now |
71
72
  | `stale-resolver-references` | ready | now |
72
73
  | `seal-agent-meta` | ready | next |
@@ -79,7 +80,7 @@ under pressure.
79
80
 
80
81
  ### 🟠 The first ten minutes, for somebody who has not read the docs
81
82
 
82
- `setup-and-first-run` · 19 item(s), 13 startable
83
+ `setup-and-first-run` · 22 item(s), 16 startable
83
84
 
84
85
  `https://github.com/gald33/switchboard/issues/86` · `https://github.com/gald33/switchboard/issues/88` · `https://github.com/gald33/switchboard/issues/89` · `docs/environments.md`
85
86
 
@@ -101,9 +102,12 @@ wrong says so.
101
102
 
102
103
  | item | status | priority |
103
104
  |---|---|---|
105
+ | `first-contact-needs-a-key-it-cannot-have` | ready | now |
106
+ | `inbox-consumes-what-it-cannot-open` | ready | now |
104
107
  | `automatic-session-checkpoints` | ready | next |
105
108
  | `connect-failure-message` | done | next |
106
109
  | `cross-key-rendezvous` | ready | next |
110
+ | `discovery-is-uneven-and-delivery-is-unknowable` | ready | next |
107
111
  | `init-writes-rooms-file` | ready | next |
108
112
  | `known-rooms-address-book` | done | next |
109
113
  | `a-lobby-derived-from-the-key` | ready | — |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-switchboard
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: An ephemeral orchestration hub for AI coding agents: presence, leases, messaging and a shared blackboard, all with TTLs.
5
5
  Project-URL: Homepage, https://github.com/gald33/switchboard
6
6
  Project-URL: Issues, https://github.com/gald33/switchboard/issues
@@ -81,6 +81,29 @@ live exactly as long as the work does.
81
81
  That's the whole model. Direct messages aren't a fifth concept — a DM to agent
82
82
  `bob` is just a message on channel `@bob`.
83
83
 
84
+ ### A message is half an exchange
85
+
86
+ Agents are turn-based. They run, they end, and something else starts them
87
+ later. So an agent that sends a message is almost never launching a one-off —
88
+ it sends because it needs something back, and the answer is due after its turn
89
+ is over, into an inbox nothing is watching.
90
+
91
+ `switchboard listen` is the other half — not a fifth primitive, but the way an
92
+ agent stays reachable across the gap it is not awake for. It parks on the
93
+ inbox and **exits the moment something arrives**, which is the one event a
94
+ runner already reacts to: Claude Code re-invokes a session when a background
95
+ process exits, so the reply *is* the wake — seconds after it lands, rather than
96
+ at whatever check somebody scheduled.
97
+
98
+ ```bash
99
+ switchboard dm reviewer-agent "0142 is mine — does that clash with yours?"
100
+ switchboard listen --until forecast:p50 # as a background process, before the turn ends
101
+ ```
102
+
103
+ `say`, `dm` and `whisper` report where each end stands — whether a listener is
104
+ parked for you, and for the agent you sent to — so "sent" is never mistaken for
105
+ "will be answered into something that is listening".
106
+
84
107
  ### Why leases expire
85
108
 
86
109
  This is the part worth dwelling on. A conventional "claim" — a row in a table,
@@ -306,12 +329,20 @@ Agents then get these as native tools:
306
329
  | `say` / `dm` / `inbox` / `history` | channel and direct messaging |
307
330
  | `board_set` / `board_get` / `board_list` | shared scratch space |
308
331
  | `checkin` | heartbeat + renew leases + drain inbox, in one call |
332
+ | *(CLI only)* `switchboard listen` | park until a reply arrives, then exit so the runner wakes the session |
309
333
  | `session_handoff` / `session_import` / `session_resume` | move a whole Claude Code session to another environment |
310
334
 
311
335
  `checkin` is the one that matters most in practice: a single tool call that
312
336
  keeps the agent alive, renews everything it holds, and hands back anything
313
337
  other agents said since last time.
314
338
 
339
+ The listener has no MCP tool on purpose: parking is a *process* that outlives
340
+ the tool call, so it is the CLI run in the background — with the mechanism the
341
+ runner itself tracks, not `&` or `nohup`, which park and wake nobody. `say`,
342
+ `dm` and `whisper` return a `listener` field saying whether one is parked for
343
+ each end of the message, so an agent finds out before its turn ends rather than
344
+ after.
345
+
315
346
  See [`docs/claude-code.md`](docs/claude-code.md) for the full setup including a
316
347
  `SessionStart` hook that registers the agent automatically and a `Stop` hook
317
348
  that releases its leases.
@@ -43,6 +43,29 @@ live exactly as long as the work does.
43
43
  That's the whole model. Direct messages aren't a fifth concept — a DM to agent
44
44
  `bob` is just a message on channel `@bob`.
45
45
 
46
+ ### A message is half an exchange
47
+
48
+ Agents are turn-based. They run, they end, and something else starts them
49
+ later. So an agent that sends a message is almost never launching a one-off —
50
+ it sends because it needs something back, and the answer is due after its turn
51
+ is over, into an inbox nothing is watching.
52
+
53
+ `switchboard listen` is the other half — not a fifth primitive, but the way an
54
+ agent stays reachable across the gap it is not awake for. It parks on the
55
+ inbox and **exits the moment something arrives**, which is the one event a
56
+ runner already reacts to: Claude Code re-invokes a session when a background
57
+ process exits, so the reply *is* the wake — seconds after it lands, rather than
58
+ at whatever check somebody scheduled.
59
+
60
+ ```bash
61
+ switchboard dm reviewer-agent "0142 is mine — does that clash with yours?"
62
+ switchboard listen --until forecast:p50 # as a background process, before the turn ends
63
+ ```
64
+
65
+ `say`, `dm` and `whisper` report where each end stands — whether a listener is
66
+ parked for you, and for the agent you sent to — so "sent" is never mistaken for
67
+ "will be answered into something that is listening".
68
+
46
69
  ### Why leases expire
47
70
 
48
71
  This is the part worth dwelling on. A conventional "claim" — a row in a table,
@@ -268,12 +291,20 @@ Agents then get these as native tools:
268
291
  | `say` / `dm` / `inbox` / `history` | channel and direct messaging |
269
292
  | `board_set` / `board_get` / `board_list` | shared scratch space |
270
293
  | `checkin` | heartbeat + renew leases + drain inbox, in one call |
294
+ | *(CLI only)* `switchboard listen` | park until a reply arrives, then exit so the runner wakes the session |
271
295
  | `session_handoff` / `session_import` / `session_resume` | move a whole Claude Code session to another environment |
272
296
 
273
297
  `checkin` is the one that matters most in practice: a single tool call that
274
298
  keeps the agent alive, renews everything it holds, and hands back anything
275
299
  other agents said since last time.
276
300
 
301
+ The listener has no MCP tool on purpose: parking is a *process* that outlives
302
+ the tool call, so it is the CLI run in the background — with the mechanism the
303
+ runner itself tracks, not `&` or `nohup`, which park and wake nobody. `say`,
304
+ `dm` and `whisper` return a `listener` field saying whether one is parked for
305
+ each end of the message, so an agent finds out before its turn ends rather than
306
+ after.
307
+
277
308
  See [`docs/claude-code.md`](docs/claude-code.md) for the full setup including a
278
309
  `SessionStart` hook that registers the agent automatically and a `Stop` hook
279
310
  that releases its leases.
@@ -151,22 +151,28 @@ cloud sessions, and in CI. Switchboard is how you coordinate with them.
151
151
  which is worth interrupting for in a way general channel traffic is not.
152
152
  On this CLI it is a line after `say` and `whisper` (and a field under
153
153
  `--json`), printed only when something is actually waiting.
154
- - **If you are ending a turn while still waiting on another agent**, read
154
+ - **Before ending a turn in which you sent anything you need an answer to,
155
+ arm the listener.** A message here is the opening of a conversation, not a
156
+ one-off: you send because you need something back, and the answer arrives on
157
+ the other agent's schedule — after this turn has ended, into an inbox no
158
+ process is watching. Run `switchboard listen --until forecast:p50` as a
159
+ background process. It parks on your inbox and exits when something arrives,
160
+ and a runner that re-invokes a session when a background process exits —
161
+ Claude Code does — wakes you seconds after the message lands rather than at
162
+ the next scheduled check. `say`, `dm` and `whisper` tell you where you
163
+ stand: a `listener` field on the MCP results and a line on the CLI, saying
164
+ whether one is parked for you and for them. `--until` is when to give up and
165
+ come back empty; without one it parks indefinitely, which is a promise to be
166
+ reachable that nothing keeps. It peeks rather than drains, so still call
167
+ `inbox` yourself when you wake, and it exits on the first message, so arm it
168
+ again if you are still waiting. It takes the flags every command takes, so
169
+ `-w` or `--invite` parks it in another room for cross-repo work.
170
+ - **If you cannot park one** — no background mechanism your runner tracks, or
171
+ a wait that outlives this session — read
155
172
  `.claude/skills/switchboard-coordinate/SKILL.md` for how to schedule a
156
- check-in instead of leaving the wait unbounded `unread_dms` only helps
173
+ check-in instead of leaving the wait unbounded. `unread_dms` only helps
157
174
  while you are still making tool calls, and nothing else will interrupt an
158
175
  idle session.
159
- - **If the thing you are waiting for is a message**, arm the listener before
160
- the turn ends: run `switchboard listen --until forecast:p50` as a background
161
- process. It parks on your inbox and exits when something arrives, and a
162
- runner that re-invokes a session when a background process exits — Claude
163
- Code does — wakes you seconds after the message lands rather than at the
164
- next scheduled check. `--until` is when to give up and come back empty;
165
- without one it parks indefinitely, which is a promise to be reachable that
166
- nothing keeps. It peeks rather than drains, so still call `inbox` yourself
167
- when you wake, and it exits on the first message, so arm it again if you are
168
- still waiting. It takes the flags every command takes, so `-w` or `--invite`
169
- parks it in another room for cross-repo work.
170
176
  - **Optionally, when a message precedes a stretch of heads-down work**, pass
171
177
  `execution_class` (a short label like "coding") and `effort`
172
178
  (`low`/`medium`/`high`) to `say`/`dm`/`checkin`/`inbox`. Your runtime turns
@@ -249,12 +255,19 @@ guarded hook degrades to a no-op.
249
255
  The `Stop` hook is a convenience, not a requirement — leases expire on their
250
256
  own. Releasing eagerly just frees the resource sooner.
251
257
 
252
- ## 6. Optional: be woken by a message
258
+ ## 6. Be woken by a message
253
259
 
254
260
  Everything above assumes the session is awake. A message that lands after the
255
261
  turn ends is delivered to nobody: the session only reaches for its inbox when
256
262
  it is its turn, and nothing about a hub can make a stopped session run.
257
263
 
264
+ That is not an edge case, which is why this section is not optional. An agent
265
+ sends because it needs something back — a review, a verdict, a lease it was
266
+ told to wait for — so most turns that send end mid-wait, and the answer is due
267
+ after the turn is over. The listener is the piece that makes the second half of
268
+ the exchange reachable. `say`, `dm` and `whisper` say where you stand on every
269
+ send: whether a listener is parked for you, and for the agent you sent to.
270
+
258
271
  What *can* is the runner. Claude Code re-invokes a session when a background
259
272
  process exits, so a process that parks on the inbox and exits when something
260
273
  arrives turns a message into a wake. `init` installs one beside the hooks in
@@ -454,18 +467,87 @@ under exactly one key, because `claude --resume` refuses to choose between
454
467
  duplicates. Other direct messages the read consumed come back as `other`
455
468
  rather than being lost.
456
469
 
457
- Local → cloud → local is then three commands and no hub change: `session
458
- handoff` on the laptop, `session receive` in the cloud session followed by
459
- `claude --resume`, and the same pair back when the cloud side is done. The
460
- hub saw two sealed values it cannot name — the pointer's discriminator
461
- travels inside the sealed body and the message type stays `note`, so not
462
- even the row type says what moved.
470
+ Local → cloud → local is three commands and no hub change: `session handoff`
471
+ on the laptop, `session receive` in the cloud session followed by `claude
472
+ --resume`, and the same pair back when the cloud side is done. The hub saw
473
+ two sealed values it cannot name — the pointer's discriminator travels inside
474
+ the sealed body and the message type stays `note`, so not even the row type
475
+ says what moved.
476
+
477
+ **One account, though — see below.** Cloud → local has been run end to end.
478
+ The other direction was run and *failed*, for a reason that is not
479
+ Switchboard's and not fixable here.
480
+
481
+ ### A session belongs to the account that made it
482
+
483
+ A transcript can carry a `bridge-session` record naming the account that
484
+ owned it. Restoring one under a different account installs and resumes
485
+ normally and then comes back **empty**: Claude Code loads the file, sees the
486
+ mismatch, and appends
487
+
488
+ ```json
489
+ {"type": "history-suppression", "cause": "restored_owner_mismatch", "vetoedAgainstAccountUuid": "…"}
490
+ ```
491
+
492
+ instead of replaying the conversation. Observed moving a bridged laptop
493
+ session into a cloud container: 14.7 MB and 36 files arrived with a matching
494
+ sha256, `claude --resume` bound the right id, and the conversation was not
495
+ there.
496
+
497
+ Nothing in the capsule causes this and nothing in it can prevent it — the
498
+ bytes are carried and verified faithfully, and the refusal happens after
499
+ Claude Code has already read them. `session resume` detects the record and
500
+ says so rather than blaming the transport; re-running does not help, because
501
+ the veto is a property of the transcript and the account.
502
+
503
+ **Which sessions carry the marker (observed).** Not most of them. Of six
504
+ transcripts compared on one machine — a plain `claude -p` session, a cloud
505
+ session, three sessions Claude Code started itself, and the bridged one — only
506
+ the bridged session had a `bridge-session` record at all. The cloud session
507
+ did not, and it is a remote session with its own `cse_…` id, so this is not
508
+ local versus cloud: the marker rides on the *bridge*, and it names the account
509
+ that owns the session on the other end of it.
510
+
511
+ So the boundary a handoff cannot cross is an *account*, and only a session
512
+ bridged under one carries the evidence. An ordinary session moves between your
513
+ own environments as the matrix says. A bridged one moves its bytes and not its
514
+ conversation unless the destination is signed in as the same account —
515
+ including where "the same person" is signed into two accounts, which is what
516
+ produced the observation above.
463
517
 
464
518
  `switchboard session export -o FILE` and `switchboard session import FILE`
465
519
  are the two halves with no hub between them: a capsule file, created `0600`
466
520
  because it is as private as the transcript it carries, that you move however
467
521
  you like.
468
522
 
523
+ ### Checkpointing, for the session that does not end politely
524
+
525
+ A handoff saves a session that ends *deliberately*. The first real transfer
526
+ survived only because the cloud agent chose to publish before it finished; a
527
+ container that dies takes its context with it, and an explicit command cannot
528
+ help a session that never got to run one.
529
+
530
+ So `switchboard init` asks, once, whether a session should checkpoint itself
531
+ when it ends. Say yes and the stop hook publishes the transcript as a handoff
532
+ addressed to nobody: sealed to the room, expiring on its own, collectable by
533
+ anyone holding the key — which in practice means your own other machines.
534
+ Publishing happens after the hook releases this agent's leases, so the capsule
535
+ records an agent that holds nothing, which is true once the session is over,
536
+ and it ends in `|| true` because a session finishing must never fail on an
537
+ unreachable hub.
538
+
539
+ It is asked rather than assumed because it is real traffic and real storage: a
540
+ transcript can be megabytes and every session end would publish one. The
541
+ default is no, a non-interactive run leaves it off, and the answer is baked
542
+ into the hook script rather than read at run time, since a hook runs as a plain
543
+ shell and never sees `.mcp.json`'s environment.
544
+
545
+ Collecting stays explicit for now. `switchboard session receive <id>` takes a
546
+ checkpoint by id, and which one a *starting* session should collect
547
+ automatically is an open question — see the `automatic-session-checkpoints`
548
+ roadmap item, which would rather leave it undecided than guess wrong about
549
+ whose conversation lands on your disk.
550
+
469
551
  ### What a handoff cannot reach, and what to send instead
470
552
 
471
553
  A handoff lands a transcript on a filesystem. That covers every environment
@@ -73,22 +73,28 @@ sessions, and in CI. Switchboard is how you coordinate with them.
73
73
  which is worth interrupting for in a way general channel traffic is not.
74
74
  On this CLI it is a line after `say` and `whisper` (and a field under
75
75
  `--json`), printed only when something is actually waiting.
76
- - **If you are ending a turn while still waiting on another agent**, read
76
+ - **Before ending a turn in which you sent anything you need an answer to,
77
+ arm the listener.** A message here is the opening of a conversation, not a
78
+ one-off: you send because you need something back, and the answer arrives on
79
+ the other agent's schedule — after this turn has ended, into an inbox no
80
+ process is watching. Run `switchboard listen --until forecast:p50` as a
81
+ background process. It parks on your inbox and exits when something arrives,
82
+ and a runner that re-invokes a session when a background process exits —
83
+ Claude Code does — wakes you seconds after the message lands rather than at
84
+ the next scheduled check. `say`, `dm` and `whisper` tell you where you
85
+ stand: a `listener` field on the MCP results and a line on the CLI, saying
86
+ whether one is parked for you and for them. `--until` is when to give up and
87
+ come back empty; without one it parks indefinitely, which is a promise to be
88
+ reachable that nothing keeps. It peeks rather than drains, so still call
89
+ `inbox` yourself when you wake, and it exits on the first message, so arm it
90
+ again if you are still waiting. It takes the flags every command takes, so
91
+ `-w` or `--invite` parks it in another room for cross-repo work.
92
+ - **If you cannot park one** — no background mechanism your runner tracks, or
93
+ a wait that outlives this session — read
77
94
  `.claude/skills/switchboard-coordinate/SKILL.md` for how to schedule a
78
- check-in instead of leaving the wait unbounded `unread_dms` only helps
95
+ check-in instead of leaving the wait unbounded. `unread_dms` only helps
79
96
  while you are still making tool calls, and nothing else will interrupt an
80
97
  idle session.
81
- - **If the thing you are waiting for is a message**, arm the listener before
82
- the turn ends: run `switchboard listen --until forecast:p50` as a background
83
- process. It parks on your inbox and exits when something arrives, and a
84
- runner that re-invokes a session when a background process exits — Claude
85
- Code does — wakes you seconds after the message lands rather than at the
86
- next scheduled check. `--until` is when to give up and come back empty;
87
- without one it parks indefinitely, which is a promise to be reachable that
88
- nothing keeps. It peeks rather than drains, so still call `inbox` yourself
89
- when you wake, and it exits on the first message, so arm it again if you are
90
- still waiting. It takes the flags every command takes, so `-w` or `--invite`
91
- parks it in another room for cross-repo work.
92
98
  - **Optionally, when a message precedes a stretch of heads-down work**, pass
93
99
  `execution_class` (a short label like "coding") and `effort`
94
100
  (`low`/`medium`/`high`) to `say`/`dm`/`checkin`/`inbox`. Your runtime turns
@@ -123,6 +123,22 @@ Each message carries a monotonically increasing `seq` alongside its cursor
123
123
  position, so an agent that wants to defend against acting on the same message
124
124
  twice — after a retried call, say — can key on `seq` rather than on content.
125
125
 
126
+ ### Sending is half of it
127
+
128
+ Long-polling covers a wait inside a turn. Most waits are not: an agent sends
129
+ because it needs something back, and the answer is due after the sender's turn
130
+ has ended — into a cursor position nothing is advancing.
131
+
132
+ `switchboard listen` is the half that makes the rest of the exchange reachable.
133
+ It parks on the inbox and exits the moment something arrives, which is the one
134
+ event a runner already reacts to, so the reply itself becomes the wake. It
135
+ peeks rather than drains, so the woken session still reads its `inbox` and the
136
+ cursor stays the session's to advance. While parked it heartbeats a
137
+ `listener/<agent-id>` entry on the blackboard, which is why `say`, `dm` and
138
+ `whisper` can tell each sender whether an answer will reach anybody: parked is
139
+ a live process saying so now, and it expires on its own when that process
140
+ stops. See [claude-code.md](claude-code.md#6-be-woken-by-a-message).
141
+
126
142
  ## Blackboard
127
143
 
128
144
  A key/value space for things too big to be a message and too transient to be a
@@ -19,7 +19,7 @@ switchboard serve --port 8787
19
19
  You should see:
20
20
 
21
21
  ```
22
- switchboard 2.2.2 → http://127.0.0.1:8787 db=switchboard.db
22
+ switchboard 2.3.0 → http://127.0.0.1:8787 db=switchboard.db
23
23
  ```
24
24
 
25
25
  Leave it running.
@@ -47,7 +47,7 @@ export SWITCHBOARD_AGENT_ID=beta
47
47
 
48
48
  ```bash
49
49
  $ switchboard health
50
- { "ok": true, "version": "2.2.2", "auth": true }
50
+ { "ok": true, "version": "2.3.0", "auth": true }
51
51
 
52
52
  $ switchboard register -c build
53
53
  registered local-main-yourhost (local) in demo
@@ -47,6 +47,24 @@ No transcript was modified. No path translation was needed. The destination
47
47
  needs neither the repo path nor the repo contents to *resume the conversation*;
48
48
  it needs them only for whatever the conversation does next.
49
49
 
50
+ **One boundary this matrix never crossed (observed later, in production).**
51
+ Every case above moved a session owned by one account — between config dirs,
52
+ between directories, between machines, but never between *accounts*. A
53
+ transcript can carry a `bridge-session` record naming its owner, and restoring
54
+ one under a different account installs, resumes under the right id, and comes
55
+ back empty; Claude Code appends
56
+ `{"type":"history-suppression","cause":"restored_owner_mismatch"}` rather than
57
+ replaying it. So "portable as a single opaque file" holds within an account
58
+ and not across one, and the row above that reads "the real, live 537 KB
59
+ session that ran this investigation" passed because it was *our own*.
60
+
61
+ Comparing six transcripts on one machine afterwards (**observed**): only a
62
+ bridged session carries the record. A plain `claude -p` session, a cloud
63
+ session with its own `cse_…` id, and three sessions Claude Code started itself
64
+ had no owner field anywhere. So the marker is not a property of every session
65
+ and not of cloud sessions — it rides on the bridge, and the account it names
66
+ is the one on the far side.
67
+
50
68
  ## 1. Where Claude Code stores sessions (observed)
51
69
 
52
70
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-switchboard"
3
- version = "2.2.2"
3
+ version = "2.3.0"
4
4
  description = "An ephemeral orchestration hub for AI coding agents: presence, leases, messaging and a shared blackboard, all with TTLs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"