agent-switchboard 2.2.0__tar.gz → 2.2.1__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.
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/PKG-INFO +1 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/adaptive-timing.md +1 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/claude-code.md +59 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/concepts.md +13 -10
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/quickstart.md +3 -3
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/upgrading.md +44 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/viewer.md +10 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/index.html +7 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/render.js +50 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/pyproject.toml +1 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/__init__.py +1 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/claude_session.py +24 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/cli.py +240 -4
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/client.py +45 -25
- agent_switchboard-2.2.1/src/switchboard/desktop.py +292 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/handoff.py +7 -2
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/mcp_server.py +8 -1
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/conftest.py +6 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_claude_session.py +30 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_config.py +59 -18
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_dash_ids.py +43 -2
- agent_switchboard-2.2.1/tests/test_desktop.py +211 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_handoff.py +18 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_web_page.py +49 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.dockerignore +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/ci.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/deploy.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/pages.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/publish-viewer.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/publish.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.github/workflows/roadmap.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/.gitignore +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/AGENTS.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/ARCS.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/CONTRIBUTING.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/Dockerfile +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/LICENSE +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/demo/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/demo/chess.sh +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/demo/run.sh +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docker-compose.yml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/api.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/codex-cli.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/deployment.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/drills.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/encryption.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/environments.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/images/viewer.png +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/layers.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/managed-hub.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/model.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/seam.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/testing.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/docs/why-this-exists.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/examples/coordinated_worker.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/session-capsule/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/session-capsule/cc.sh +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/session-capsule/claude_session_capsule.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/pyproject.toml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/__init__.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/__main__.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/viewer.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/switchboard-open.js +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/switchboard-room.js +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/ROADMAP.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/arcs/distribution.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/arcs/hub-boundary.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/arcs/setup-and-first-run.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/arcs/ttl-ceilings.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/a-lobby-derived-from-the-key.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/abuse-control-after-authorization.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/board-ttl-ceiling.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/ci-workspace-is-public.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/clients-that-cannot-post.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/connect-failure-message.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/cross-key-rendezvous.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/every-silent-failure-looks-like-a-quiet-room.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/hooks-warning-false-positive.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/hub-origin-reachable-bypassing-the-edge.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/identity-rebinds-on-branch-change.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/init-writes-rooms-file.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/intermittent-suite-failure.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/joining-agent-sees-empty-inbox.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/known-rooms-address-book.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/one-resolved-context-across-surfaces.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/presence-ttl-is-not-one-size.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/provisioned-token-is-stale-and-nothing-says-so.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/publish-hub-container-image.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/read-only-rooms.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/robots-policy-for-public-hosts.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/roles-and-authority-between-agents.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/seal-agent-meta.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/selective-wake-for-the-listener.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/stale-resolver-references.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/stale-token-in-session-env.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/standing-checks-that-nothing-runs.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/timing-cold-start-in-ephemeral-environments.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/ttl-clamped-silently.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/unread-dms-not-shown-outside-mcp.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/roadmap/items/write-parity-across-surfaces.yaml +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/scripts/deploy.sh +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/auth.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/config.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/crypto.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/drill.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/guidance.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/holds.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/invite.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/knownrooms.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/load.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/notify.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/peers.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/rendezvous.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/rooms.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/scripts/wake-on-message.sh +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/server.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/signing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/001-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/002-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/003-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/004-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/005-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/006-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/007-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/008-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/009-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/010-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/011-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/012-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/013-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/014-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/015-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/016-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/017-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/018-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/019-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/020-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/021-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/022-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/023-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/024-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/history/025-SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/skill/switchboard-coordinate/SKILL.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/spec.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/store.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/testing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/timing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/src/switchboard/writekey.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/README.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/__init__.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/analysis.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/economy.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/flow.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/llm.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/manager.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/report.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/run.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier1.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier1_labour.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier1_rounds.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_bound.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_built.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_free.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_paid.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_spend.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed1_told.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/tier2_seed41_bound.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter/traders.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter_experiment.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/barter_llm_experiment.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_harness.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/RESULTS.md +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_A_s31337.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_A_s4242.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_A_s7777.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_B_s31337.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_B_s4242.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_B_s7777.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_C_s31337.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_C_s4242.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/prompt_C_s7777.txt +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_A_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_A_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_A_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_B_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_B_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_B_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_C_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_C_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_haiku_C_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_A_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_A_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_A_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_B_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_B_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_B_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_C_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_C_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_opus_C_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_A_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_A_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_A_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_B_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_B_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_B_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_C_s31337.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_C_s4242.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_pilot/response_sonnet_C_s7777.json +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/tier2_run.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/experiments/timing_experiment.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_api.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_arrive.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_auth.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_barter.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_barter_llm.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_board.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_checkin.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_config.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_global_flags.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_init.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_serve.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_session.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cli_timing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_client_invite.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_cors_write_headers.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_crypto.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_declare.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_deployment.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_drill.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_evening_replayed.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_example_viewer.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_failure_messages.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_help.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_invite.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_isolated_hub.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_known_rooms.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_leave_a_ghost.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_load.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_lobby.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_mcp.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_mcp_session.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_notify.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_rendezvous.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_rooms.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_spec.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_store.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_surface_parity.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_testing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_tier2_harness.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_timing.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_timing_experiment.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_transient_failures.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_version.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_wake_listener.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_web_invite.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_web_reader.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_web_snapshot.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_whisper.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_workflow_token.py +0 -0
- {agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/tests/test_write_keys.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-switchboard
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
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
|
|
@@ -48,7 +48,7 @@ is never a commitment, and no agent is required to obey another's forecast.
|
|
|
48
48
|
machines. It is shared by every repo on that machine, but every read and
|
|
49
49
|
write is keyed by `(agent_id, workspace)` — and `workspace` defaults to
|
|
50
50
|
the git remote's org/repo — so history never bleeds from one repo into
|
|
51
|
-
another's forecasts. `agent_id` derives from kind/
|
|
51
|
+
another's forecasts. `agent_id` derives from kind/hostname/session, so the
|
|
52
52
|
same person on two laptops builds two independent histories that converge
|
|
53
53
|
separately. All of that is deliberate: the premise is that timing is
|
|
54
54
|
local and personal, and a shared or committed history would be neither.
|
|
@@ -466,6 +466,65 @@ are the two halves with no hub between them: a capsule file, created `0600`
|
|
|
466
466
|
because it is as private as the transcript it carries, that you move however
|
|
467
467
|
you like.
|
|
468
468
|
|
|
469
|
+
### If you work in the desktop app
|
|
470
|
+
|
|
471
|
+
Installing the transcript is the whole job for a CLI user and half of one for
|
|
472
|
+
everybody else. The desktop app keeps its own list of sessions beside the
|
|
473
|
+
transcripts, so a conversation whose transcript is installed but whose record
|
|
474
|
+
is not exists on disk and is invisible in the app — which is exactly what
|
|
475
|
+
happened the first time a session was handed from a cloud agent to a laptop:
|
|
476
|
+
it resumed perfectly on the command line and could not be found in the app the
|
|
477
|
+
recipient actually used.
|
|
478
|
+
|
|
479
|
+
So `switchboard init` asks, once, on a machine that runs the app: should a
|
|
480
|
+
session handed to you also appear in its sidebar? Say yes and `receive` writes
|
|
481
|
+
the record too. The question is asked at setup rather than per handoff for two
|
|
482
|
+
reasons — it writes into another program's store, which is a thing to agree to
|
|
483
|
+
deliberately, and the account and project the row is filed under are ids that
|
|
484
|
+
app assigns, discoverable only where it runs. A cloud runner has neither, so
|
|
485
|
+
the step is silently skipped there.
|
|
486
|
+
|
|
487
|
+
`switchboard session register <id>` does it for a session already installed,
|
|
488
|
+
and `session unregister <id>` is the undo. Prefer that over deleting the row
|
|
489
|
+
inside the app: in-app delete and archive both write a release marker next to
|
|
490
|
+
the transcript which the CLI acts on, while unregister removes only the record.
|
|
491
|
+
|
|
492
|
+
Two things worth knowing. The app reads its store at launch, so a registered
|
|
493
|
+
session appears after the next restart rather than immediately. And this is an
|
|
494
|
+
undocumented format belonging to another program: `switchboard.desktop` says
|
|
495
|
+
which app version it was verified against, keeps every detail in that one
|
|
496
|
+
module, and answers "not here" rather than guessing when the shape is not what
|
|
497
|
+
it expects.
|
|
498
|
+
|
|
499
|
+
### What travels, and what a reader can take out
|
|
500
|
+
|
|
501
|
+
The subagent transcripts in the `<id>/` sidecar travel by default, and that
|
|
502
|
+
is the trade worth naming because it is not the obvious one. They are most
|
|
503
|
+
of a capsule's bytes — three quarters of it is normal — and none of its
|
|
504
|
+
resume: `claude --resume` reads the main transcript alone, so the sidecar
|
|
505
|
+
costs the receiver no context at cold start. What it buys is retrievability.
|
|
506
|
+
A subagent's transcript is finished work, often millions of tokens of it,
|
|
507
|
+
that the receiving session can go and read *if it needs to* rather than
|
|
508
|
+
re-derive at model prices. Bytes on a board with a ten-minute TTL are the
|
|
509
|
+
cheap side of that trade, so the default is on.
|
|
510
|
+
|
|
511
|
+
`--no-subagents` (`no_subagents` on `session_handoff`) drops them, for a slow
|
|
512
|
+
link or a receiver that only needs the conversation continued. The capsule
|
|
513
|
+
records that it did, as `omitted_subagent_files`, because a session that
|
|
514
|
+
never spawned a subagent and one whose subagents were left behind both
|
|
515
|
+
arrive as a single file, and a receiver deciding whether to ask again needs
|
|
516
|
+
to tell those apart.
|
|
517
|
+
|
|
518
|
+
A capsule can also be collected with no Switchboard on the machine at all.
|
|
519
|
+
The [viewer](../extras/viewer/) already holds the plaintext — it fetched the
|
|
520
|
+
board entry and opened it with the room key, which is why a sealed value is
|
|
521
|
+
legible there — so a board entry it could open offers a **save** button that
|
|
522
|
+
writes the value out as a file. Feed that file to `switchboard session
|
|
523
|
+
import FILE` wherever the tooling *is* installed. It asks the hub for
|
|
524
|
+
nothing extra, and it is the one path to a handoff for a human with a
|
|
525
|
+
browser and no checkout. Where the page could not open a value it offers
|
|
526
|
+
nothing: a file of envelope bytes would look like the value and is not.
|
|
527
|
+
|
|
469
528
|
### What is trusted, and what is not
|
|
470
529
|
|
|
471
530
|
A transcript is instructions. Whoever resumes it runs a conversation
|
|
@@ -43,20 +43,23 @@ An agent registers itself (id, name, kind, branch, current task) and then
|
|
|
43
43
|
heartbeats. If it stops heartbeating, it disappears from the roster within its
|
|
44
44
|
TTL — two minutes by default.
|
|
45
45
|
|
|
46
|
-
The agent id is inferred from `kind`,
|
|
47
|
-
|
|
48
|
-
appearing as a stranger.
|
|
49
|
-
|
|
46
|
+
The agent id is inferred from `kind`, hostname and a hash of the session id,
|
|
47
|
+
so a session that resumes reclaims its own identity — its leases, its read
|
|
48
|
+
cursor — rather than appearing as a stranger. Two sessions on one machine get
|
|
49
|
+
different ids, because the session is part of the derivation. Override it with
|
|
50
|
+
`SWITCHBOARD_AGENT_ID` when you want something specific.
|
|
50
51
|
|
|
51
52
|
`kind` is auto-detected as `ci` (in GitHub Actions), `cloud` (in a remote
|
|
52
53
|
Claude Code session or a Codespace) or `local`.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
**The branch is deliberately not part of the id.** It is registered as its own
|
|
56
|
+
field, which is what `dm` resolves a typed branch name against — so it says
|
|
57
|
+
where an agent is *now* and follows it across a checkout, while the id stays
|
|
58
|
+
put. An id has to outlive the session's whole life: it is what the signer
|
|
59
|
+
socket is keyed on, what the roster stores a public key against, what holds a
|
|
60
|
+
lease and what owns a read cursor. Folding the branch in meant that checking
|
|
61
|
+
one out — the most ordinary thing an agent does — minted a second identity and
|
|
62
|
+
orphaned all four.
|
|
60
63
|
|
|
61
64
|
## Leases
|
|
62
65
|
|
|
@@ -19,7 +19,7 @@ switchboard serve --port 8787
|
|
|
19
19
|
You should see:
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
switchboard 2.2.
|
|
22
|
+
switchboard 2.2.1 → http://127.0.0.1:8787 db=switchboard.db
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Leave it running.
|
|
@@ -35,7 +35,7 @@ export SWITCHBOARD_WORKSPACE=demo
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
In the second terminal, give the agent a distinct identity so you can tell them
|
|
38
|
-
apart (normally this is inferred from
|
|
38
|
+
apart (normally this is inferred from your host and session):
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
export SWITCHBOARD_AGENT_ID=beta
|
|
@@ -47,7 +47,7 @@ export SWITCHBOARD_AGENT_ID=beta
|
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
49
|
$ switchboard health
|
|
50
|
-
{ "ok": true, "version": "2.2.
|
|
50
|
+
{ "ok": true, "version": "2.2.1", "auth": true }
|
|
51
51
|
|
|
52
52
|
$ switchboard register -c build
|
|
53
53
|
registered local-main-yourhost (local) in demo
|
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
What changes for you between releases that are not backwards compatible, in
|
|
4
4
|
the order you will hit them. Everything not listed here kept its behaviour.
|
|
5
5
|
|
|
6
|
+
## 2.2.0 → 2.2.1
|
|
7
|
+
|
|
8
|
+
**Your agent ids change once.** An id was `kind-branch-host-session`; the
|
|
9
|
+
branch is gone from it, so it is now `kind-host-session`. The branch was in
|
|
10
|
+
there from when it was the only thing telling two sessions apart — the
|
|
11
|
+
session suffix took that job and left the branch behind as decoration, and
|
|
12
|
+
decoration with teeth: an id is what the signer socket is keyed on, what the
|
|
13
|
+
roster stores a public key against, what holds a lease and what owns a read
|
|
14
|
+
cursor. Checking out a branch, the most ordinary thing an agent does, minted
|
|
15
|
+
a second identity and orphaned all four.
|
|
16
|
+
|
|
17
|
+
Nothing to do about it. On first announce after upgrading, an agent appears
|
|
18
|
+
under its new id; the row under the old one stops being refreshed and lapses
|
|
19
|
+
on the presence TTL like any other. **Leases and read cursors held by the old
|
|
20
|
+
id are not transferred** — a lease expires on its own TTL (15 minutes by
|
|
21
|
+
default) and a cursor's cost is one repeated read. If you cannot wait for a
|
|
22
|
+
lease to lapse, release it before upgrading, or pin `SWITCHBOARD_AGENT_ID`
|
|
23
|
+
across the upgrade to keep the id you had.
|
|
24
|
+
|
|
25
|
+
Branch-based lookup is unaffected: `dm` and the other recipient resolvers
|
|
26
|
+
match the roster's own `branch` field, registered on every announce, which
|
|
27
|
+
follows an agent across a checkout in a way an id baked at startup never
|
|
28
|
+
did. A name still says the branch if you registered it that way.
|
|
29
|
+
|
|
30
|
+
`client.identity_drift_warning` is now `client.rootless_warning`, and warns
|
|
31
|
+
about a different thing. It existed for this bug's directory half — outside a
|
|
32
|
+
checkout the branch read as `detached` and the id moved — which no longer
|
|
33
|
+
happens. What still drifts outside a checkout is the *workspace*, derived
|
|
34
|
+
from the directory when there is no remote to read, which puts the agent in a
|
|
35
|
+
different room entirely; that is what the renamed function now says.
|
|
36
|
+
|
|
37
|
+
Also fixed: an option value beginning with `-` no longer has to be escaped
|
|
38
|
+
by hand. `switchboard session receive --from <agent-id>` and `board list
|
|
39
|
+
--prefix <key>` died on "expected one argument" for about one id in
|
|
40
|
+
sixty-six, because ids and board keys are base64url and argparse will not
|
|
41
|
+
read a dash-leading token as an option's value. Both now work as typed —
|
|
42
|
+
the same repair `dm -yLAoQ63…` already had for positionals, extended to the
|
|
43
|
+
other half of the problem.
|
|
44
|
+
|
|
45
|
+
Additive in the same release: `--no-subagents` on `session export`,
|
|
46
|
+
`handoff` and `publish` (`no_subagents` on `session_handoff`) leaves the
|
|
47
|
+
subagent transcripts out of a capsule, and the viewer can save an opened
|
|
48
|
+
board value to a file. Neither changes an existing default.
|
|
49
|
+
|
|
6
50
|
## 2.1.0 → 2.2.0
|
|
7
51
|
|
|
8
52
|
Additive. A whole Claude Code session can be moved between environments
|
|
@@ -104,6 +104,16 @@ the rest: the whole key, who wrote it and when, and a value that keeps its own
|
|
|
104
104
|
line breaks instead of being folded to fit. The sidebar itself now widens with
|
|
105
105
|
the screen rather than staying 340px on every one.
|
|
106
106
|
|
|
107
|
+
An opened entry also offers **save**, which writes the value out as a file
|
|
108
|
+
named after its key. The page is already holding the plaintext — it fetched
|
|
109
|
+
the entry and opened it with the room key, which is the whole reason a sealed
|
|
110
|
+
value is legible here — so this asks the hub for nothing and needs no CLI. It
|
|
111
|
+
is how a person with a browser and no checkout collects a
|
|
112
|
+
[session capsule](claude-code.md#7-hand-a-whole-session-to-another-environment)
|
|
113
|
+
and feeds it to `switchboard session import` somewhere else. A value this page
|
|
114
|
+
could not open offers no button: a file of envelope bytes would look like the
|
|
115
|
+
value and is not.
|
|
116
|
+
|
|
107
117
|
Each of the three side panels says how much is inside it and folds away, and
|
|
108
118
|
what you fold stays folded. On a window too narrow for two columns they stop
|
|
109
119
|
being a stack under the conversation — where, the talking being long and never
|
{agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/index.html
RENAMED
|
@@ -139,6 +139,13 @@
|
|
|
139
139
|
.detail .meta { color: var(--dim); font-size: 12px; margin: 4px 0 14px;
|
|
140
140
|
display: flex; flex-wrap: wrap; gap: 4px 12px; }
|
|
141
141
|
.detail .body, .detail pre { font-size: 13px; }
|
|
142
|
+
/* Quiet until wanted: a board value is usually read, not taken, and a
|
|
143
|
+
download button competing with the value would say otherwise. */
|
|
144
|
+
.detail .meta .save { border: 1px solid var(--line); border-radius: 6px;
|
|
145
|
+
background: transparent; color: var(--dim); font: inherit;
|
|
146
|
+
font-size: 12px; padding: 0 8px; cursor: pointer; }
|
|
147
|
+
.detail .meta .save:hover, .detail .meta .save:focus-visible {
|
|
148
|
+
color: var(--accent); border-color: var(--accent); }
|
|
142
149
|
.detail .nothing { color: var(--dim); font-size: 13px; }
|
|
143
150
|
/* A key is the row's own text, so it reads as text — and as something that
|
|
144
151
|
does something only where it does something. */
|
{agent_switchboard-2.2.0 → agent_switchboard-2.2.1}/extras/viewer/switchboard_viewer/web/render.js
RENAMED
|
@@ -513,6 +513,8 @@ function drawDetail(s) {
|
|
|
513
513
|
<span>${who(entry.updated_by)}</span>
|
|
514
514
|
<span>${stamp(entry.updated_at)}</span>
|
|
515
515
|
<span>expires in ${countdown(entry.expires_in, s.generated_at)}</span>
|
|
516
|
+
${readable(entry) ? `<button class="save" type="button"
|
|
517
|
+
data-save="${esc(entry.key)}">save</button>` : ""}
|
|
516
518
|
</div>
|
|
517
519
|
${bodyHtml(entry.value)}`;
|
|
518
520
|
if (pane.dataset.html !== html) {
|
|
@@ -521,6 +523,49 @@ function drawDetail(s) {
|
|
|
521
523
|
}
|
|
522
524
|
}
|
|
523
525
|
|
|
526
|
+
/** Whether this page holds the entry's plaintext at all.
|
|
527
|
+
*
|
|
528
|
+
* A value that could not be opened arrives as `null` — the builder drops the
|
|
529
|
+
* envelope rather than let ciphertext render as content — and that is the
|
|
530
|
+
* condition to check, not `sealed`, which only says whether the *key* stayed
|
|
531
|
+
* a blinded token. Without a room key nothing is unblinded, so `sealed` is
|
|
532
|
+
* false for every entry in a room this page cannot read: exactly the rooms
|
|
533
|
+
* where offering to save one would write out a file that is not the value.
|
|
534
|
+
*/
|
|
535
|
+
function readable(entry) {
|
|
536
|
+
return Boolean(entry) && entry.value !== null && entry.value !== undefined;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** Hand the reader the value they are already looking at, as a file.
|
|
540
|
+
*
|
|
541
|
+
* The page has the plaintext: it fetched the entry and opened it with the
|
|
542
|
+
* room key, which is the whole reason a sealed value is legible here at all.
|
|
543
|
+
* Writing it out asks the hub for nothing and needs no checkout, no CLI and
|
|
544
|
+
* no agent — which is the point. A session capsule saved from here goes
|
|
545
|
+
* straight into `switchboard session import`, so a human with a browser can
|
|
546
|
+
* collect a handoff that no tooling on their machine could.
|
|
547
|
+
*
|
|
548
|
+
* Named after the key so the file says what it is, and `.json` when the
|
|
549
|
+
* value is structured, because everything worth saving from a board is.
|
|
550
|
+
*/
|
|
551
|
+
function saveEntry(key) {
|
|
552
|
+
const entry = (last?.board || []).find((e) => e.key === key);
|
|
553
|
+
if (!readable(entry)) return;
|
|
554
|
+
const structured = typeof entry.value !== "string";
|
|
555
|
+
const text = structured ? JSON.stringify(entry.value, null, 2) : entry.value;
|
|
556
|
+
const name = `${key.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "")
|
|
557
|
+
|| "board-entry"}${structured ? ".json" : ".txt"}`;
|
|
558
|
+
const url = URL.createObjectURL(
|
|
559
|
+
new Blob([text], { type: structured ? "application/json" : "text/plain" }));
|
|
560
|
+
const a = document.createElement("a");
|
|
561
|
+
a.href = url;
|
|
562
|
+
a.download = name;
|
|
563
|
+
a.click();
|
|
564
|
+
// Same turn would race the click on some browsers; the next one is safe and
|
|
565
|
+
// still prompt enough that nothing accumulates.
|
|
566
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
567
|
+
}
|
|
568
|
+
|
|
524
569
|
export function render(s, { onRoom, onClose } = {}) {
|
|
525
570
|
last = s;
|
|
526
571
|
if (onRoom || onClose) handlers = { onRoom, onClose };
|
|
@@ -784,6 +829,11 @@ export function render(s, { onRoom, onClose } = {}) {
|
|
|
784
829
|
scopeChanged();
|
|
785
830
|
return;
|
|
786
831
|
}
|
|
832
|
+
const save = e.target.closest("[data-save]");
|
|
833
|
+
if (save) {
|
|
834
|
+
saveEntry(save.dataset.save);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
787
837
|
const entry = e.target.closest("[data-board]");
|
|
788
838
|
if (entry) {
|
|
789
839
|
opened = entry.dataset.board;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agent-switchboard"
|
|
3
|
-
version = "2.2.
|
|
3
|
+
version = "2.2.1"
|
|
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"
|
|
@@ -268,6 +268,7 @@ def package(
|
|
|
268
268
|
*,
|
|
269
269
|
config_dir: str | os.PathLike[str] | None = None,
|
|
270
270
|
cwd: str | os.PathLike[str] | None = None,
|
|
271
|
+
subagents: bool = True,
|
|
271
272
|
) -> dict[str, Any]:
|
|
272
273
|
"""Build a capsule for a session found under this machine's config dir.
|
|
273
274
|
|
|
@@ -275,6 +276,19 @@ def package(
|
|
|
275
276
|
(rare: it means the session was already imported here from elsewhere).
|
|
276
277
|
Nothing is modified; the transcript is read as it is, which inside a live
|
|
277
278
|
session means up to the tool call that asked for the export.
|
|
279
|
+
|
|
280
|
+
``subagents`` carries the sidecar directory — every subagent's own
|
|
281
|
+
transcript — and defaults to on, which looks expensive and is not. Those
|
|
282
|
+
files are most of the bytes (three quarters, on a session that ran a
|
|
283
|
+
couple of workflows) and none of them are needed to resume: ``--resume``
|
|
284
|
+
reads the main transcript alone, so they cost the receiving model no
|
|
285
|
+
context at all. What they are is the work already done, at rest: one
|
|
286
|
+
session here carried 153 subagent transcripts holding some five million
|
|
287
|
+
tokens of finished analysis for six megabytes on the wire. Dropping them
|
|
288
|
+
saves a transfer nobody notices and throws away the answer to "why did we
|
|
289
|
+
reject that finding?", which otherwise has to be bought again. Turn it
|
|
290
|
+
off for a slow link or a receiver that only needs the conversation; the
|
|
291
|
+
capsule then says so, rather than arriving quietly short.
|
|
278
292
|
"""
|
|
279
293
|
session_id = valid_session_id(session_id)
|
|
280
294
|
cfg = _resolve_config_dir(config_dir)
|
|
@@ -307,10 +321,14 @@ def package(
|
|
|
307
321
|
})
|
|
308
322
|
|
|
309
323
|
add(transcript, f"{session_id}.jsonl")
|
|
324
|
+
omitted = 0
|
|
310
325
|
if sidecar.is_dir():
|
|
311
326
|
for path in sorted(sidecar.rglob("*")):
|
|
312
327
|
if path.is_file() and not path.is_symlink():
|
|
313
|
-
|
|
328
|
+
if subagents:
|
|
329
|
+
add(path, f"{session_id}/{path.relative_to(sidecar).as_posix()}")
|
|
330
|
+
else:
|
|
331
|
+
omitted += 1
|
|
314
332
|
|
|
315
333
|
meta = transcript_metadata(transcript)
|
|
316
334
|
return {
|
|
@@ -323,6 +341,11 @@ def package(
|
|
|
323
341
|
"exported_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
324
342
|
"stats": {k: meta[k] for k in ("records", "user_messages", "assistant_messages")},
|
|
325
343
|
"files": files,
|
|
344
|
+
# Recorded rather than inferred from the file list: a session that
|
|
345
|
+
# never spawned a subagent and one whose subagents were dropped both
|
|
346
|
+
# arrive with a single file, and a receiver deciding whether to ask
|
|
347
|
+
# for them again needs to tell those apart.
|
|
348
|
+
**({"omitted_subagent_files": omitted} if omitted else {}),
|
|
326
349
|
}
|
|
327
350
|
|
|
328
351
|
|