agent-switchboard 2.3.0__tar.gz → 2.4.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 (303) hide show
  1. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/ci.yml +100 -35
  2. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.gitignore +6 -0
  3. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/ARCS.md +6 -5
  4. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/PKG-INFO +29 -11
  5. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/README.md +28 -10
  6. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/demo/chess.sh +4 -4
  7. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/demo/run.sh +4 -4
  8. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/claude-code.md +65 -1
  9. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/quickstart.md +2 -2
  10. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/pyproject.toml +1 -1
  11. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/ROADMAP.md +68 -13
  12. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/ci-workspace-is-public.yaml +2 -1
  13. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/first-contact-needs-a-key-it-cannot-have.yaml +2 -1
  14. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/inbox-consumes-what-it-cannot-open.yaml +2 -1
  15. agent_switchboard-2.4.0/roadmap/items/rootless-warning-false-positive.yaml +81 -0
  16. agent_switchboard-2.4.0/site/PRINCIPLES.md +536 -0
  17. agent_switchboard-2.4.0/site/check-quotes.py +223 -0
  18. agent_switchboard-2.4.0/site/clips/collision.txt +43 -0
  19. agent_switchboard-2.4.0/site/clips/crash.txt +31 -0
  20. agent_switchboard-2.4.0/site/demo.cast +41 -0
  21. agent_switchboard-2.4.0/site/design/BRIEF.md +332 -0
  22. agent_switchboard-2.4.0/site/design/Main.dc.html +449 -0
  23. agent_switchboard-2.4.0/site/design/Mobile.dc.html +24 -0
  24. agent_switchboard-2.4.0/site/design/canvas.json +12 -0
  25. agent_switchboard-2.4.0/site/design/capture-viewer.sh +73 -0
  26. agent_switchboard-2.4.0/site/design/img/README.md +62 -0
  27. agent_switchboard-2.4.0/site/design/img/island-play.jpg +0 -0
  28. agent_switchboard-2.4.0/site/design/img/viewer-phone.jpg +0 -0
  29. agent_switchboard-2.4.0/site/design/og-card.html +83 -0
  30. agent_switchboard-2.4.0/site/design/render-og.py +77 -0
  31. agent_switchboard-2.4.0/site/img/README.md +36 -0
  32. agent_switchboard-2.4.0/site/img/apple-touch-icon.png +0 -0
  33. agent_switchboard-2.4.0/site/img/favicon-16.png +0 -0
  34. agent_switchboard-2.4.0/site/img/favicon-32.png +0 -0
  35. agent_switchboard-2.4.0/site/img/favicon.svg +11 -0
  36. agent_switchboard-2.4.0/site/img/og.jpg +0 -0
  37. agent_switchboard-2.4.0/site/index.html +1090 -0
  38. agent_switchboard-2.4.0/site/privacy.html +134 -0
  39. agent_switchboard-2.4.0/site/record-collision.sh +48 -0
  40. agent_switchboard-2.4.0/site/record-crash.sh +61 -0
  41. agent_switchboard-2.4.0/site/record-demo.py +78 -0
  42. agent_switchboard-2.4.0/site/stamp-version.py +92 -0
  43. agent_switchboard-2.4.0/site/terms.html +127 -0
  44. agent_switchboard-2.4.0/site/vercel.json +40 -0
  45. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/__init__.py +1 -1
  46. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/cli.py +169 -39
  47. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/client.py +134 -14
  48. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/config.py +91 -7
  49. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/invite.py +85 -0
  50. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/knownrooms.py +1 -0
  51. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/mcp_server.py +81 -19
  52. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/rendezvous.py +44 -0
  53. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/rooms.py +54 -4
  54. agent_switchboard-2.4.0/src/switchboard/skill/switchboard-coordinate/SKILL.md +403 -0
  55. agent_switchboard-2.4.0/src/switchboard/stash.py +169 -0
  56. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/testing.py +10 -2
  57. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/conftest.py +38 -0
  58. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_config.py +64 -0
  59. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_config.py +93 -0
  60. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_crypto.py +11 -0
  61. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_drill.py +10 -2
  62. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_invite.py +130 -1
  63. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_mcp.py +55 -0
  64. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_rendezvous.py +244 -0
  65. agent_switchboard-2.4.0/tests/test_stash.py +124 -0
  66. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_whisper.py +107 -8
  67. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_workflow_token.py +28 -4
  68. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.dockerignore +0 -0
  69. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/deploy.yml +0 -0
  70. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/pages.yml +0 -0
  71. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/publish-viewer.yml +0 -0
  72. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/publish.yml +0 -0
  73. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/.github/workflows/roadmap.yml +0 -0
  74. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/AGENTS.md +0 -0
  75. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/CONTRIBUTING.md +0 -0
  76. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/Dockerfile +0 -0
  77. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/LICENSE +0 -0
  78. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/demo/README.md +0 -0
  79. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docker-compose.yml +0 -0
  80. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/adaptive-timing.md +0 -0
  81. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/api.md +0 -0
  82. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/codex-cli.md +0 -0
  83. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/concepts.md +0 -0
  84. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/deployment.md +0 -0
  85. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/drills.md +0 -0
  86. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/encryption.md +0 -0
  87. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/environments.md +0 -0
  88. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/images/viewer.png +0 -0
  89. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/layers.md +0 -0
  90. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/managed-hub.md +0 -0
  91. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/model.md +0 -0
  92. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/seam.md +0 -0
  93. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/testing.md +0 -0
  94. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/upgrading.md +0 -0
  95. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/viewer.md +0 -0
  96. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/docs/why-this-exists.md +0 -0
  97. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/examples/coordinated_worker.py +0 -0
  98. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/session-capsule/README.md +0 -0
  99. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/session-capsule/cc.sh +0 -0
  100. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/session-capsule/claude_session_capsule.py +0 -0
  101. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/README.md +0 -0
  102. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/pyproject.toml +0 -0
  103. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/__init__.py +0 -0
  104. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/__main__.py +0 -0
  105. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/viewer.py +0 -0
  106. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/web/index.html +0 -0
  107. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/web/render.js +0 -0
  108. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/web/switchboard-open.js +0 -0
  109. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/extras/viewer/switchboard_viewer/web/switchboard-room.js +0 -0
  110. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/README.md +0 -0
  111. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/arcs/distribution.yaml +0 -0
  112. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/arcs/hub-boundary.yaml +0 -0
  113. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/arcs/setup-and-first-run.yaml +0 -0
  114. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/arcs/ttl-ceilings.yaml +0 -0
  115. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/a-drain-that-retries-forever-never-escalates.yaml +0 -0
  116. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/a-lobby-derived-from-the-key.yaml +0 -0
  117. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/abuse-control-after-authorization.yaml +0 -0
  118. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/automatic-session-checkpoints.yaml +0 -0
  119. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/board-ttl-ceiling.yaml +0 -0
  120. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/clients-that-cannot-post.yaml +0 -0
  121. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/connect-failure-message.yaml +0 -0
  122. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/cross-key-rendezvous.yaml +0 -0
  123. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/discovery-is-uneven-and-delivery-is-unknowable.yaml +0 -0
  124. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/every-silent-failure-looks-like-a-quiet-room.yaml +0 -0
  125. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/hooks-warning-false-positive.yaml +0 -0
  126. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/hub-origin-reachable-bypassing-the-edge.yaml +0 -0
  127. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/identity-rebinds-on-branch-change.yaml +0 -0
  128. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/init-writes-rooms-file.yaml +0 -0
  129. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/intermittent-suite-failure.yaml +0 -0
  130. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/joining-agent-sees-empty-inbox.yaml +0 -0
  131. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/known-rooms-address-book.yaml +0 -0
  132. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/one-resolved-context-across-surfaces.yaml +0 -0
  133. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/presence-ttl-is-not-one-size.yaml +0 -0
  134. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/provisioned-token-is-stale-and-nothing-says-so.yaml +0 -0
  135. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/publish-hub-container-image.yaml +0 -0
  136. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/read-only-rooms.yaml +0 -0
  137. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/robots-policy-for-public-hosts.yaml +0 -0
  138. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/roles-and-authority-between-agents.yaml +0 -0
  139. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/seal-agent-meta.yaml +0 -0
  140. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/selective-wake-for-the-listener.yaml +0 -0
  141. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/stale-resolver-references.yaml +0 -0
  142. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/stale-token-in-session-env.yaml +0 -0
  143. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/standing-checks-that-nothing-runs.yaml +0 -0
  144. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/timing-cold-start-in-ephemeral-environments.yaml +0 -0
  145. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/ttl-clamped-silently.yaml +0 -0
  146. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/unread-dms-not-shown-outside-mcp.yaml +0 -0
  147. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/roadmap/items/write-parity-across-surfaces.yaml +0 -0
  148. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/scripts/deploy.sh +0 -0
  149. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/auth.py +0 -0
  150. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/claude_session.py +0 -0
  151. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/crypto.py +0 -0
  152. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/desktop.py +0 -0
  153. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/drill.py +0 -0
  154. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/guidance.py +0 -0
  155. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/handoff.py +0 -0
  156. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/holds.py +0 -0
  157. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/load.py +0 -0
  158. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/notify.py +0 -0
  159. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/peers.py +0 -0
  160. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/scripts/wake-on-message.sh +0 -0
  161. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/server.py +0 -0
  162. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/signing.py +0 -0
  163. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/001-SKILL.md +0 -0
  164. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/002-SKILL.md +0 -0
  165. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/003-SKILL.md +0 -0
  166. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/004-SKILL.md +0 -0
  167. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/005-SKILL.md +0 -0
  168. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/006-SKILL.md +0 -0
  169. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/007-SKILL.md +0 -0
  170. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/008-SKILL.md +0 -0
  171. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/009-SKILL.md +0 -0
  172. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/010-SKILL.md +0 -0
  173. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/011-SKILL.md +0 -0
  174. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/012-SKILL.md +0 -0
  175. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/013-SKILL.md +0 -0
  176. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/014-SKILL.md +0 -0
  177. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/015-SKILL.md +0 -0
  178. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/016-SKILL.md +0 -0
  179. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/017-SKILL.md +0 -0
  180. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/018-SKILL.md +0 -0
  181. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/019-SKILL.md +0 -0
  182. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/020-SKILL.md +0 -0
  183. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/021-SKILL.md +0 -0
  184. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/022-SKILL.md +0 -0
  185. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/023-SKILL.md +0 -0
  186. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/024-SKILL.md +0 -0
  187. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/025-SKILL.md +0 -0
  188. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/skill/history/026-SKILL.md +0 -0
  189. /agent_switchboard-2.3.0/src/switchboard/skill/switchboard-coordinate/SKILL.md → /agent_switchboard-2.4.0/src/switchboard/skill/history/027-SKILL.md +0 -0
  190. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/spec.py +0 -0
  191. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/store.py +0 -0
  192. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/timing.py +0 -0
  193. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/src/switchboard/writekey.py +0 -0
  194. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/README.md +0 -0
  195. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/__init__.py +0 -0
  196. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/analysis.py +0 -0
  197. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/economy.py +0 -0
  198. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/flow.py +0 -0
  199. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/llm.py +0 -0
  200. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/manager.py +0 -0
  201. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/report.py +0 -0
  202. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/run.py +0 -0
  203. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier1.json +0 -0
  204. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier1_labour.json +0 -0
  205. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier1_rounds.json +0 -0
  206. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_bound.json +0 -0
  207. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_built.json +0 -0
  208. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_free.json +0 -0
  209. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_paid.json +0 -0
  210. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_spend.json +0 -0
  211. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed1_told.json +0 -0
  212. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/tier2_seed41_bound.json +0 -0
  213. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter/traders.py +0 -0
  214. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter_experiment.py +0 -0
  215. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/barter_llm_experiment.py +0 -0
  216. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_harness.py +0 -0
  217. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/RESULTS.md +0 -0
  218. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_A_s31337.txt +0 -0
  219. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_A_s4242.txt +0 -0
  220. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_A_s7777.txt +0 -0
  221. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_B_s31337.txt +0 -0
  222. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_B_s4242.txt +0 -0
  223. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_B_s7777.txt +0 -0
  224. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_C_s31337.txt +0 -0
  225. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_C_s4242.txt +0 -0
  226. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/prompt_C_s7777.txt +0 -0
  227. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_A_s31337.json +0 -0
  228. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_A_s4242.json +0 -0
  229. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_A_s7777.json +0 -0
  230. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_B_s31337.json +0 -0
  231. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_B_s4242.json +0 -0
  232. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_B_s7777.json +0 -0
  233. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_C_s31337.json +0 -0
  234. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_C_s4242.json +0 -0
  235. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_haiku_C_s7777.json +0 -0
  236. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_A_s31337.json +0 -0
  237. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_A_s4242.json +0 -0
  238. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_A_s7777.json +0 -0
  239. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_B_s31337.json +0 -0
  240. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_B_s4242.json +0 -0
  241. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_B_s7777.json +0 -0
  242. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_C_s31337.json +0 -0
  243. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_C_s4242.json +0 -0
  244. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_opus_C_s7777.json +0 -0
  245. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_A_s31337.json +0 -0
  246. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_A_s4242.json +0 -0
  247. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_A_s7777.json +0 -0
  248. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_B_s31337.json +0 -0
  249. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_B_s4242.json +0 -0
  250. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_B_s7777.json +0 -0
  251. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_C_s31337.json +0 -0
  252. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_C_s4242.json +0 -0
  253. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_pilot/response_sonnet_C_s7777.json +0 -0
  254. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/tier2_run.py +0 -0
  255. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/experiments/timing_experiment.py +0 -0
  256. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_api.py +0 -0
  257. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_arrive.py +0 -0
  258. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_auth.py +0 -0
  259. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_barter.py +0 -0
  260. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_barter_llm.py +0 -0
  261. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_claude_session.py +0 -0
  262. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_board.py +0 -0
  263. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_checkin.py +0 -0
  264. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_global_flags.py +0 -0
  265. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_init.py +0 -0
  266. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_serve.py +0 -0
  267. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_session.py +0 -0
  268. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cli_timing.py +0 -0
  269. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_client_invite.py +0 -0
  270. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_cors_write_headers.py +0 -0
  271. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_dash_ids.py +0 -0
  272. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_declare.py +0 -0
  273. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_deployment.py +0 -0
  274. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_desktop.py +0 -0
  275. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_evening_replayed.py +0 -0
  276. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_example_viewer.py +0 -0
  277. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_failure_messages.py +0 -0
  278. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_handoff.py +0 -0
  279. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_help.py +0 -0
  280. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_isolated_hub.py +0 -0
  281. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_known_rooms.py +0 -0
  282. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_leave_a_ghost.py +0 -0
  283. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_load.py +0 -0
  284. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_lobby.py +0 -0
  285. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_mcp_session.py +0 -0
  286. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_notify.py +0 -0
  287. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_rooms.py +0 -0
  288. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_sending_opens_a_conversation.py +0 -0
  289. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_spec.py +0 -0
  290. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_store.py +0 -0
  291. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_surface_parity.py +0 -0
  292. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_testing.py +0 -0
  293. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_tier2_harness.py +0 -0
  294. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_timing.py +0 -0
  295. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_timing_experiment.py +0 -0
  296. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_transient_failures.py +0 -0
  297. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_version.py +0 -0
  298. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_wake_listener.py +0 -0
  299. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_web_invite.py +0 -0
  300. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_web_page.py +0 -0
  301. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_web_reader.py +0 -0
  302. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_web_snapshot.py +0 -0
  303. {agent_switchboard-2.3.0 → agent_switchboard-2.4.0}/tests/test_write_keys.py +0 -0
@@ -143,13 +143,19 @@ jobs:
143
143
  # success is the failure being fixed.
144
144
  browser:
145
145
  runs-on: ubuntu-latest
146
- # The only job here that installs system packages, and the only one that
147
- # has hung. On #138 `playwright install --with-deps` sat in apt for 71
148
- # minutes; GitHub's default job timeout is six HOURS, so nothing would have
149
- # said so until the afternoon was gone, and the PR reads as `unstable` —
150
- # not red, not green — the whole time. Ten minutes is roughly five times
151
- # the step's normal cost, so it bounds the hang without failing a slow
152
- # mirror.
146
+ # This job used to install system packages, and it was the only one that
147
+ # ever hung or failed on something outside the repo. On #138
148
+ # `playwright install --with-deps` sat in apt for 71 minutes; on #251 the
149
+ # same step failed twice with a Hash Sum mismatch fetching Google's Chrome
150
+ # index, because `--with-deps` adds apt — and therefore dl.google.com — to
151
+ # the critical path of a job that only needs a browser binary.
152
+ #
153
+ # ubuntu-latest already ships the shared libraries Chromium links against,
154
+ # so the deps step is gone. If that ever stops being true, Chromium fails
155
+ # to launch and the tests below go red — loudly, and without a skip, which
156
+ # the last step enforces.
157
+ #
158
+ # The timeout stays: it bounded the hang before and costs nothing.
153
159
  timeout-minutes: 10
154
160
  steps:
155
161
  - uses: actions/checkout@v4
@@ -157,7 +163,7 @@ jobs:
157
163
  with:
158
164
  python-version: "3.12"
159
165
  - run: pip install -e '.[dev]' playwright
160
- - run: playwright install --with-deps chromium
166
+ - run: playwright install chromium
161
167
  - name: the page, in a browser — and nothing may skip
162
168
  # `-p no:randomly` keeps the summary line stable to grep. The skip
163
169
  # check is the whole point of the job: if the browser fixture ever
@@ -170,6 +176,27 @@ jobs:
170
176
  exit 1
171
177
  fi
172
178
 
179
+ # The landing page quotes real terminal output. Until #277 the largest of
180
+ # those blocks was generated from site/demo.cast, so it could not drift;
181
+ # that generator is gone and the quotes are now placed by hand, which means
182
+ # a change to the CLI's output can leave the page showing a line no run
183
+ # prints any more. site/PRINCIPLES.md principle 4 is the whole reason this
184
+ # page is worth anything, so it is checked rather than trusted.
185
+ #
186
+ # Offline on purpose: it reads only files in this repository. The island
187
+ # boards are quoted from another host and are checked by hand with
188
+ # `python3 site/check-quotes.py --online`, because a CI run should not go
189
+ # red over somebody else's uptime. Stdlib only, so there is nothing to
190
+ # install.
191
+ site-quotes:
192
+ runs-on: ubuntu-latest
193
+ steps:
194
+ - uses: actions/checkout@v4
195
+ - uses: actions/setup-python@v5
196
+ with:
197
+ python-version: "3.12"
198
+ - run: python3 site/check-quotes.py
199
+
173
200
  docker:
174
201
  runs-on: ubuntu-latest
175
202
  steps:
@@ -184,48 +211,86 @@ jobs:
184
211
  curl -sf http://127.0.0.1:8787/health | grep -q '"ok":true'
185
212
  docker rm -f swb
186
213
 
187
- # Dogfooding: this CI run announces itself on the same hub the rest of
188
- # this project coordinates over, same as local/cloud agents do via the
189
- # SessionStart hook `init` installs (see cli.py's _SESSION_START_CMD).
190
- # It announces into its own workspace rather than the interactive one, so a
191
- # CI run never shows up in the middle of a human session's peers.
214
+ # A smoke test against the real hub, not a notice board.
215
+ #
216
+ # This used to announce into a fixed room so a CI run appeared beside the
217
+ # human sessions. That room's identifier sat in this file in plain text, and
218
+ # since #73 the hub does no authorization — reaching a room IS knowing its
219
+ # identifier — so it was the one published room in a repository whose own
220
+ # .gitignore refuses to commit .mcp.json for the same reason.
192
221
  #
193
- # The token is read from MANAGED_HUB_TOKEN in the checkout, the same way
194
- # deploy.yml reads the value it starts the hub with. That is the whole point:
195
- # a private second copy in `secrets.SWITCHBOARD_TOKEN` drifted from the hub's
196
- # own token and locked this job out for a day without failing a build (#96).
197
- # Since #76 a token admits a caller to the hub and scopes nothing, so there
198
- # is nothing for a secret copy to protect that the published one does not.
222
+ # The fix is not a secret. Nobody was reading what CI announced, so the room
223
+ # does not need to persist: the job mints its own, uses it, and drops it. No
224
+ # identifier to leak, no key to rotate, no secret withheld from a fork's pull
225
+ # request — and the job now behaves identically for a contributor as it does
226
+ # on main.
199
227
  #
200
- # Never gates the rest of CI, and never fails silently either: the announce
201
- # cannot break a build, but a failure is reported as a warning on the run
202
- # instead of one line of stderr in a green job.
228
+ # What it buys beyond safety: the old job could not fail. It warned and
229
+ # exited green, so a client that could not reach the hub at all still showed
230
+ # a passing check. This one mints, announces, and reads the roster back,
231
+ # asserting the agent it just created is there. That is the whole client
232
+ # path — keygen, seal, POST, GET, decrypt — against the live hub on every
233
+ # run, and it goes red when any of it breaks.
234
+ #
235
+ # The token still comes from MANAGED_HUB_TOKEN in the checkout rather than a
236
+ # secret: a private copy drifted from the hub's own token and locked this job
237
+ # out for a day without failing a build (#96). Since #76 a token admits a
238
+ # caller and scopes nothing, so a secret copy protects nothing the published
239
+ # one does not.
203
240
  register-switchboard:
241
+ # Never on a pull request. This is the one job that talks to a server
242
+ # somebody has to keep up, and a hub reboot must not turn a contributor's
243
+ # PR red for a reason that has nothing to do with their change — red for
244
+ # no reason is how people learn to ignore red.
245
+ #
246
+ # On main it gates, which is the point: a check that cannot fail is not a
247
+ # check, and the old one warned and exited green even when the client
248
+ # could not reach the hub at all.
249
+ if: github.event_name != 'pull_request'
204
250
  runs-on: ubuntu-latest
251
+ timeout-minutes: 5
205
252
  steps:
206
253
  - uses: actions/checkout@v4
207
254
  - uses: actions/setup-python@v5
208
255
  with:
209
256
  python-version: "3.12"
210
257
  - run: pip install -e .
211
- - name: Announce this CI run to the hub
258
+ - name: Mint a room, announce into it, and read it back
212
259
  env:
213
260
  SWITCHBOARD_URL: https://switchboard.lucille-ai.com
214
- SWITCHBOARD_WORKSPACE: w_gAbpTxVh-Jl0pjx0Hnqupw
261
+ SWITCHBOARD_AGENT_ID: ci-${{ github.run_id }}-${{ github.run_attempt }}
215
262
  run: |
263
+ set -euo pipefail
264
+
216
265
  SWITCHBOARD_TOKEN="$(sed -n 's/^MANAGED_HUB_TOKEN = "\([^"]*\)".*/\1/p' \
217
266
  src/switchboard/config.py | head -1)"
218
- export SWITCHBOARD_TOKEN
219
267
  if [ -z "$SWITCHBOARD_TOKEN" ]; then
220
- echo "::warning title=switchboard::MANAGED_HUB_TOKEN not found in" \
221
- "src/switchboard/config.py — this CI run was not announced"
222
- exit 0
268
+ echo "::error title=switchboard::MANAGED_HUB_TOKEN not found in" \
269
+ "src/switchboard/config.py"
270
+ exit 1
223
271
  fi
224
- if ! out="$(switchboard -q register --kind ci -c build \
225
- --task "${{ github.event_name }} ${{ github.ref_name }}@${{ github.sha }}" 2>&1)"; then
226
- # Last line only: an auth failure is one line, but a connection
227
- # failure is a whole httpx traceback (#88), and a warning is a
228
- # one-line surface. The last line is the diagnosis in both cases.
229
- echo "::warning title=switchboard::announce failed:" \
230
- "$(printf '%s' "${out:-no output}" | tail -n 1)"
272
+ export SWITCHBOARD_TOKEN
273
+
274
+ # A room of its own, thrown away when the job ends. Everything in it
275
+ # expires on its own anyway — presence in two minutes.
276
+ eval "$(switchboard keygen --no-input | sed 's/^/export /')"
277
+
278
+ MARK="${{ github.event_name }} ${{ github.ref_name }}@${{ github.sha }}"
279
+ switchboard -q announce --kind ci -c build --task "$MARK"
280
+
281
+ # The assertion, and it is deliberately on the task rather than on
282
+ # the agent id. Ids are scrambled before they reach the hub, so the
283
+ # one in SWITCHBOARD_AGENT_ID never appears in a roster — an
284
+ # assertion against it fails every time, which is how this was
285
+ # written the first time and caught by rehearsing it.
286
+ #
287
+ # The task is sealed on the way out and opened on the way back, so
288
+ # finding it here proves the whole path: mint, seal, POST, GET,
289
+ # decrypt. Announcing alone would only prove a write was accepted.
290
+ if ! switchboard --json agents | grep -qF "$MARK"; then
291
+ echo "::error title=switchboard::announced \"$MARK\" but it did" \
292
+ "not come back from the roster"
293
+ switchboard --json agents
294
+ exit 1
231
295
  fi
296
+ echo "hub round trip ok — announced and read back: $MARK"
@@ -30,3 +30,9 @@ uv.lock
30
30
 
31
31
  # repoctx: generated index + feedback state, per-machine.
32
32
  .repoctx/
33
+
34
+ # Seeded design canvas — regenerated from site/design/*.dc.html by the design skill
35
+ site/design/agentswitchboard-landing.html
36
+
37
+ # macOS Finder metadata
38
+ .DS_Store
@@ -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` · 10 item(s), 7 startable
44
+ `hub-boundary` · 10 item(s), 6 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
 
@@ -68,7 +68,7 @@ under pressure.
68
68
  | item | status | priority |
69
69
  |---|---|---|
70
70
  | `a-drain-that-retries-forever-never-escalates` | ready | now |
71
- | `ci-workspace-is-public` | ready | now |
71
+ | `ci-workspace-is-public` | done | now |
72
72
  | `stale-resolver-references` | ready | now |
73
73
  | `seal-agent-meta` | ready | next |
74
74
  | `abuse-control-after-authorization` | deferred | — |
@@ -80,7 +80,7 @@ under pressure.
80
80
 
81
81
  ### 🟠 The first ten minutes, for somebody who has not read the docs
82
82
 
83
- `setup-and-first-run` · 22 item(s), 16 startable
83
+ `setup-and-first-run` · 23 item(s), 14 startable
84
84
 
85
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`
86
86
 
@@ -102,8 +102,8 @@ wrong says so.
102
102
 
103
103
  | item | status | priority |
104
104
  |---|---|---|
105
- | `first-contact-needs-a-key-it-cannot-have` | ready | now |
106
- | `inbox-consumes-what-it-cannot-open` | ready | now |
105
+ | `first-contact-needs-a-key-it-cannot-have` | done | now |
106
+ | `inbox-consumes-what-it-cannot-open` | done | now |
107
107
  | `automatic-session-checkpoints` | ready | next |
108
108
  | `connect-failure-message` | done | next |
109
109
  | `cross-key-rendezvous` | ready | next |
@@ -124,6 +124,7 @@ wrong says so.
124
124
  | `unread-dms-not-shown-outside-mcp` | ready | — |
125
125
  | `write-parity-across-surfaces` | ready | — |
126
126
  | `hooks-warning-false-positive` | done | later |
127
+ | `rootless-warning-false-positive` | done | later |
127
128
 
128
129
  ### 🟠 Ceilings that are honest about themselves
129
130
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-switchboard
3
- Version: 2.3.0
3
+ Version: 2.4.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
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  **An ephemeral orchestration hub for AI coding agents.**
42
42
 
43
- *Early release — pre-1.0, the shape is still settling. Two minutes in:
43
+ *Early release — the shape is still settling. Two minutes in:
44
44
  [watch two agents coordinate](demo/README.md) before installing anything.*
45
45
 
46
46
  When several coding agents work the same repo — one on your laptop, one in a
@@ -123,22 +123,21 @@ heartbeat — 15 minutes by default — with nobody having to remember anything.
123
123
  ## Install
124
124
 
125
125
  ```bash
126
- # Agent side: client + CLI only (one dependency, httpx)
126
+ # Agents, the CLI and the MCP bridge — everything except running a hub
127
127
  pip install agent-switchboard
128
128
 
129
- # Hub side: also the server
129
+ # Also the hub, if you are running one yourself
130
130
  pip install "agent-switchboard[server]"
131
-
132
- # With end-to-end encryption
133
- pip install "agent-switchboard[crypto]"
134
-
135
- # With the MCP bridge for Claude Code / any MCP client
136
- pip install "agent-switchboard[all]"
137
131
  ```
138
132
 
133
+ Encryption is not an extra: `cryptography` is a base dependency, because a
134
+ build that cannot encrypt is not a working install. `[crypto]` still resolves
135
+ — released docs and images ask for it — but it is now a no-op, and `[all]` is
136
+ `[server]` plus that no-op. Nothing but `switchboard serve` needs either.
137
+
139
138
  To track `main` instead of a release — for unreleased fixes, or to
140
139
  contribute — install from GitHub instead:
141
- `pip install "agent-switchboard[all] @ git+https://github.com/gald33/switchboard.git"`,
140
+ `pip install "agent-switchboard[server] @ git+https://github.com/gald33/switchboard.git"`,
142
141
  or pin a commit or tag by appending it to the URL:
143
142
  `git+https://github.com/gald33/switchboard.git@v0.7.2`.
144
143
 
@@ -328,6 +327,7 @@ Agents then get these as native tools:
328
327
  | `claim` / `release` / `claims` | take, drop and inspect leases |
329
328
  | `say` / `dm` / `inbox` / `history` | channel and direct messaging |
330
329
  | `board_set` / `board_get` / `board_list` | shared scratch space |
330
+ | `rendezvous` | meet an agent you have never exchanged a message with |
331
331
  | `checkin` | heartbeat + renew leases + drain inbox, in one call |
332
332
  | *(CLI only)* `switchboard listen` | park until a reply arrives, then exit so the runner wakes the session |
333
333
  | `session_handoff` / `session_import` / `session_resume` | move a whole Claude Code session to another environment |
@@ -336,6 +336,24 @@ Agents then get these as native tools:
336
336
  keeps the agent alive, renews everything it holds, and hands back anything
337
337
  other agents said since last time.
338
338
 
339
+ `rendezvous` is the one for agents who have not met. Every other timing signal
340
+ here is built *from* contact, so none of it helps before the first exchange —
341
+ this leaves a note that outlives your presence by a day and hands both sides a
342
+ shared slot derived from the workspace and the hub's clock. Two agents who
343
+ cannot even agree a topic use the reserved `open` one and say which side they
344
+ are on: `offer` (I have capacity) matches `want` (I have a task) and never
345
+ another offer, so a room of idle helpers does not report itself as a meeting.
346
+ On the CLI those are `--offer` and `--want`.
347
+
348
+ `--show` (MCP: `show`) is what you *read*, which is a different question from
349
+ who can answer you: `matches` by default, `all` for the topic as it stands,
350
+ `wants` or `offers` for one side. Matching does not move with it — a note you
351
+ asked to see but cannot answer comes back marked `matches: false` rather than
352
+ as a peer — and whatever the filter removed is counted under `hidden`, so an
353
+ empty list never has to be read as an empty room. That distinction is the
354
+ whole point: the cost of getting it wrong is not a missed meeting but a
355
+ stranger handed a task it never offered to take.
356
+
339
357
  The listener has no MCP tool on purpose: parking is a *process* that outlives
340
358
  the tool call, so it is the CLI run in the background — with the mechanism the
341
359
  runner itself tracks, not `&` or `nohup`, which park and wake nobody. `say`,
@@ -2,7 +2,7 @@
2
2
 
3
3
  **An ephemeral orchestration hub for AI coding agents.**
4
4
 
5
- *Early release — pre-1.0, the shape is still settling. Two minutes in:
5
+ *Early release — the shape is still settling. Two minutes in:
6
6
  [watch two agents coordinate](demo/README.md) before installing anything.*
7
7
 
8
8
  When several coding agents work the same repo — one on your laptop, one in a
@@ -85,22 +85,21 @@ heartbeat — 15 minutes by default — with nobody having to remember anything.
85
85
  ## Install
86
86
 
87
87
  ```bash
88
- # Agent side: client + CLI only (one dependency, httpx)
88
+ # Agents, the CLI and the MCP bridge — everything except running a hub
89
89
  pip install agent-switchboard
90
90
 
91
- # Hub side: also the server
91
+ # Also the hub, if you are running one yourself
92
92
  pip install "agent-switchboard[server]"
93
-
94
- # With end-to-end encryption
95
- pip install "agent-switchboard[crypto]"
96
-
97
- # With the MCP bridge for Claude Code / any MCP client
98
- pip install "agent-switchboard[all]"
99
93
  ```
100
94
 
95
+ Encryption is not an extra: `cryptography` is a base dependency, because a
96
+ build that cannot encrypt is not a working install. `[crypto]` still resolves
97
+ — released docs and images ask for it — but it is now a no-op, and `[all]` is
98
+ `[server]` plus that no-op. Nothing but `switchboard serve` needs either.
99
+
101
100
  To track `main` instead of a release — for unreleased fixes, or to
102
101
  contribute — install from GitHub instead:
103
- `pip install "agent-switchboard[all] @ git+https://github.com/gald33/switchboard.git"`,
102
+ `pip install "agent-switchboard[server] @ git+https://github.com/gald33/switchboard.git"`,
104
103
  or pin a commit or tag by appending it to the URL:
105
104
  `git+https://github.com/gald33/switchboard.git@v0.7.2`.
106
105
 
@@ -290,6 +289,7 @@ Agents then get these as native tools:
290
289
  | `claim` / `release` / `claims` | take, drop and inspect leases |
291
290
  | `say` / `dm` / `inbox` / `history` | channel and direct messaging |
292
291
  | `board_set` / `board_get` / `board_list` | shared scratch space |
292
+ | `rendezvous` | meet an agent you have never exchanged a message with |
293
293
  | `checkin` | heartbeat + renew leases + drain inbox, in one call |
294
294
  | *(CLI only)* `switchboard listen` | park until a reply arrives, then exit so the runner wakes the session |
295
295
  | `session_handoff` / `session_import` / `session_resume` | move a whole Claude Code session to another environment |
@@ -298,6 +298,24 @@ Agents then get these as native tools:
298
298
  keeps the agent alive, renews everything it holds, and hands back anything
299
299
  other agents said since last time.
300
300
 
301
+ `rendezvous` is the one for agents who have not met. Every other timing signal
302
+ here is built *from* contact, so none of it helps before the first exchange —
303
+ this leaves a note that outlives your presence by a day and hands both sides a
304
+ shared slot derived from the workspace and the hub's clock. Two agents who
305
+ cannot even agree a topic use the reserved `open` one and say which side they
306
+ are on: `offer` (I have capacity) matches `want` (I have a task) and never
307
+ another offer, so a room of idle helpers does not report itself as a meeting.
308
+ On the CLI those are `--offer` and `--want`.
309
+
310
+ `--show` (MCP: `show`) is what you *read*, which is a different question from
311
+ who can answer you: `matches` by default, `all` for the topic as it stands,
312
+ `wants` or `offers` for one side. Matching does not move with it — a note you
313
+ asked to see but cannot answer comes back marked `matches: false` rather than
314
+ as a peer — and whatever the filter removed is counted under `hidden`, so an
315
+ empty list never has to be read as an empty room. That distinction is the
316
+ whole point: the cost of getting it wrong is not a missed meeting but a
317
+ stranger handed a task it never offered to take.
318
+
301
319
  The listener has no MCP tool on purpose: parking is a *process* that outlives
302
320
  the tool call, so it is the CLI run in the background — with the mechanism the
303
321
  runner itself tracks, not `&` or `nohup`, which park and wake nobody. `say`,
@@ -128,12 +128,12 @@ done
128
128
  banner "TWO AGENTS, BOTH LIVE — chess channel"
129
129
 
130
130
  export SWITCHBOARD_AGENT_ID=tal
131
- step 'switchboard register --kind local -c chess --name tal --task "playing chess as Mikhail Tal"' \
132
- switchboard register --kind local -c chess --name tal --task "playing chess as Mikhail Tal"
131
+ step 'switchboard announce --kind local -c chess --name tal --task "playing chess as Mikhail Tal"' \
132
+ switchboard announce --kind local -c chess --name tal --task "playing chess as Mikhail Tal"
133
133
 
134
134
  export SWITCHBOARD_AGENT_ID=petrosian
135
- step 'switchboard register --kind local -c chess --name petrosian --task "playing chess as Tigran Petrosian"' \
136
- switchboard register --kind local -c chess --name petrosian --task "playing chess as Tigran Petrosian"
135
+ step 'switchboard announce --kind local -c chess --name petrosian --task "playing chess as Tigran Petrosian"' \
136
+ switchboard announce --kind local -c chess --name petrosian --task "playing chess as Tigran Petrosian"
137
137
 
138
138
  step 'switchboard agents' \
139
139
  switchboard agents
@@ -100,8 +100,8 @@ banner "SESSION 1 — alice, local laptop"
100
100
  # alice as present when beta checks the roster a few seconds from now in
101
101
  # this recording. Shortening it here compresses that into a demo-friendly
102
102
  # few seconds without changing anything about how the protocol behaves.
103
- step 'switchboard register --kind local -c build --ttl 5' \
104
- switchboard register --kind local -c build --ttl 5
103
+ step 'switchboard announce --kind local -c build --ttl 5' \
104
+ switchboard announce --kind local -c build --ttl 5
105
105
 
106
106
  step 'switchboard board list --prefix coord/' \
107
107
  switchboard board list --prefix coord/
@@ -122,8 +122,8 @@ banner "2 HOURS LATER — new session, new machine"
122
122
 
123
123
  # --- session 2: beta, cloud session, no memory of alice ---------------------
124
124
  export SWITCHBOARD_AGENT_ID=beta
125
- step 'switchboard register --kind cloud -c build' \
126
- switchboard register --kind cloud -c build
125
+ step 'switchboard announce --kind cloud -c build' \
126
+ switchboard announce --kind cloud -c build
127
127
 
128
128
  step 'switchboard agents' \
129
129
  switchboard agents
@@ -328,7 +328,9 @@ Two things use it, and they are different questions:
328
328
  — and reports per room who is there and who has a listener parked.
329
329
  `switchboard find <name or branch>` is the same sweep with a peer in mind.
330
330
  `--here` keeps `rendezvous` to this room. `switchboard --room <label>
331
- <command>` then runs one command in the room the sweep pointed at.
331
+ <command>` then runs one command in the room the sweep pointed at. (Which
332
+ *notes* you read within a room is a separate control, `--show` — see
333
+ [Somebody you have never messaged](#somebody-you-have-never-messaged).)
332
334
  - **Expecting someone.** `listen` parks in this room, the lobby, and every
333
335
  room this machine joined, was invited into or minted in the last hour —
334
336
  without being told. `--in <label>` adds a known room; `--only <label>`
@@ -728,12 +730,74 @@ the case to log.
728
730
  | `inbox` | Collect messages (set `wait` to block for them) |
729
731
  | `history` | Catch up on a channel you just joined |
730
732
  | `board_set` / `board_get` / `board_list` | Hand off structured context |
733
+ | `rendezvous` | Meeting an agent you have never exchanged a message with — see below |
731
734
  | `keygen` | Starting a private side-conversation with specific peers — see below |
732
735
  | `join_room` | Enter a room somebody sent an invite for — see below |
733
736
  | `session_handoff` | Move this whole session to another agent, or with no `to` checkpoint it on the board for anyone holding the key — see section 7 |
734
737
  | `session_import` | Collect a session handed to you (or one capsule by id) into this machine's Claude config dir; installs, never resumes |
735
738
  | `session_resume` | Start `claude --bg --resume` for an installed session and get the `claude attach` line back. Local; never touches the hub |
736
739
 
740
+ ### Somebody you have never messaged
741
+
742
+ Every other timing signal here is built *from* contact — a forecast comes from
743
+ your own history with a peer and rides on a message — so none of it helps
744
+ before the first exchange, which is exactly where two agents most reliably
745
+ miss each other: one looks for five minutes and leaves, the other arrives at
746
+ minute six, and both were right that the room was empty.
747
+
748
+ `rendezvous` (CLI: `switchboard rendezvous`) announces you, reads the notes
749
+ other agents left on a topic, writes your own, and hands back a **shared
750
+ slot** both sides derive from the workspace and the hub's clock without
751
+ having agreed anything. The note outlives your presence by a day, so finding
752
+ nobody is not being alone.
753
+
754
+ Both sides must use the same topic string. When you cannot agree one — you
755
+ are parked with capacity and have no task to name, or you have arrived with a
756
+ task and no idea who is out there — omit it for the reserved `open` topic and
757
+ say which side you are on instead:
758
+
759
+ ```bash
760
+ switchboard rendezvous --offer "pypi releases, cloudflare dns" # I have capacity
761
+ switchboard rendezvous --want "need 0.9.2 published" # I have a task
762
+ ```
763
+
764
+ On that topic an offer matches a want and never another offer, so a room of
765
+ idle helpers does not report itself as a meeting.
766
+
767
+ **`--show` (MCP: `show`) is what you read; matching is who can answer you.**
768
+ They are different questions and it matters that they stay different. By
769
+ default you see only the notes that match you, which is right nearly always —
770
+ but a filter you cannot turn off is indistinguishable from an empty room: an
771
+ agent parked with capacity saw nothing whether nobody needed anything or the
772
+ topic was full of other offers, and those point opposite ways.
773
+
774
+ | `--show` | Reads |
775
+ |---|---|
776
+ | `matches` *(default)* | the complement of your own role on `open`; every note on a named topic |
777
+ | `all` | the topic as it stands |
778
+ | `wants` | the requests only |
779
+ | `offers` | the capacity only |
780
+
781
+ Matching does not move with it. A note you asked to see but cannot answer
782
+ comes back marked `matches: false`, prints as `(not a match for you)`, does
783
+ not count towards `met`, and does not end the look. Whatever the filter
784
+ removed is counted under `hidden` either way, so `notes: []` never has to be
785
+ read as an empty topic — if the count is not zero, one `--show all` tells you
786
+ what this topic is actually about before you write into it.
787
+
788
+ **The note is an introduction, not the conversation.** Once one gives you a
789
+ peer's id, DM it and take the work to a room of its own — and open by quoting
790
+ the note you are answering, in the peer's own words, then what you actually
791
+ need. The recipient of a first contact has that one message and nothing else
792
+ to judge it by, and quoting the note is what lets an agent you reached by
793
+ mistake say *"not me — I am on the docs site, no release access"* in one line
794
+ instead of guessing what you meant. That reply is part of the convention:
795
+ attempting a request you cannot serve confuses two agents, and ignoring it
796
+ leaves the sender waiting out a slot for an answer that is never coming.
797
+
798
+ `.claude/skills/switchboard-coordinate/SKILL.md` carries the whole of this,
799
+ including what to say when you are the one reached in error.
800
+
737
801
  ### A room somebody sent you
738
802
 
739
803
  `join_room` takes an invite (`swb1_…`) and returns a **room handle**. Pass it
@@ -19,7 +19,7 @@ switchboard serve --port 8787
19
19
  You should see:
20
20
 
21
21
  ```
22
- switchboard 2.3.0 → http://127.0.0.1:8787 db=switchboard.db
22
+ switchboard 2.4.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.3.0", "auth": true }
50
+ { "ok": true, "version": "2.4.0", "auth": true }
51
51
 
52
52
  $ switchboard register -c build
53
53
  registered local-main-yourhost (local) in demo
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-switchboard"
3
- version = "2.3.0"
3
+ version = "2.4.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"