switchroom 0.19.47 → 0.20.0
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.
- package/dist/agent-scheduler/index.js +22 -1
- package/dist/auth-broker/index.js +26 -2
- package/dist/buzz-gateway/index.js +9207 -0
- package/dist/cli/notion-write-pretool.mjs +22 -1
- package/dist/cli/switchroom.js +91 -9
- package/dist/host-control/main.js +27 -3
- package/dist/vault/approvals/kernel-server.js +26 -2
- package/dist/vault/broker/server.js +26 -2
- package/package.json +4 -3
- package/profiles/_base/start.sh.hbs +78 -1
- package/profiles/default/CLAUDE.md.hbs +1 -1
- package/skills/dev-protocol/SKILL.md +30 -1
- package/skills/switchroom-architecture/SKILL.md +5 -0
- package/skills/switchroom-cli/SKILL.md +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +7 -4
- package/telegram-plugin/dist/gateway/gateway.js +1156 -247
- package/telegram-plugin/dist/server.js +7 -4
- package/telegram-plugin/gateway/boot-briefing-builder.ts +458 -0
- package/telegram-plugin/gateway/boot-briefing-wiring.ts +170 -0
- package/telegram-plugin/gateway/buzz-mirror.ts +329 -0
- package/telegram-plugin/gateway/buzz-type-guards.ts +34 -0
- package/telegram-plugin/gateway/channel-route.ts +272 -0
- package/telegram-plugin/gateway/gateway.ts +73 -81
- package/telegram-plugin/gateway/inbound-spool.ts +33 -1
- package/telegram-plugin/gateway/ipc-protocol.ts +81 -2
- package/telegram-plugin/gateway/ipc-server.ts +197 -2
- package/telegram-plugin/gateway/outbound-send-path.ts +37 -1
- package/telegram-plugin/gateway/pending-turn-env.ts +61 -0
- package/telegram-plugin/gateway/stream-render.ts +21 -0
- package/telegram-plugin/gateway/subagent-handback-marker.ts +12 -0
- package/telegram-plugin/gateway/user-failure-notices.ts +172 -0
- package/telegram-plugin/history.ts +15 -0
- package/telegram-plugin/llm-error-present.ts +9 -4
- package/telegram-plugin/model-unavailable.ts +4 -0
- package/telegram-plugin/operator-events.fixtures.json +12 -12
- package/telegram-plugin/operator-events.ts +81 -9
- package/telegram-plugin/session-tail.ts +7 -1
- package/telegram-plugin/tests/boot-briefing-builder.test.ts +604 -0
- package/telegram-plugin/tests/buzz-mirror.test.ts +242 -0
- package/telegram-plugin/tests/buzz-origin-stamp-gate.test.ts +159 -0
- package/telegram-plugin/tests/channel-route.test.ts +306 -0
- package/telegram-plugin/tests/inbound-spool.test.ts +47 -0
- package/telegram-plugin/tests/ipc-server-buzz-dedup.test.ts +124 -0
- package/telegram-plugin/tests/ipc-server-buzz-peer.test.ts +269 -0
- package/telegram-plugin/tests/operator-events-session-tail.test.ts +63 -0
- package/telegram-plugin/tests/operator-events.test.ts +71 -7
- package/telegram-plugin/tests/user-failure-notices.test.ts +165 -0
- package/telegram-plugin/voice-normalize-text.ts +5 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +4 -0
- package/vendor/hindsight-memory/scripts/recall.py +7 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchroom",
|
|
3
3
|
"//version": "NOT the release version — source of truth is the git tag, resolved by scripts/build.mjs:resolveVersion() (see CLAUDE.md > Standard release process). This field is stale by design and only the Layer-4 dev/non-tag fallback for build.mjs + src/cli/resolve-version.ts; do NOT bump it expecting a release to pick it up. npm-pack tarball naming needs a real version — do that as an UNCOMMITTED pack-time bump (see release step 6), never a committed one.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.20.0",
|
|
5
5
|
"description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"build": "node scripts/build.mjs",
|
|
25
25
|
"build:cli": "node scripts/build.mjs && bun build --compile --target=bun-linux-x64 --minify bin/switchroom.ts --outfile switchroom-linux-amd64",
|
|
26
26
|
"pretest": "npm run build",
|
|
27
|
-
"test": "vitest run && bun test telegram-plugin/tests/agent-state-dir-preload.test.ts telegram-plugin/tests/catch-all-forwarded-history.test.ts telegram-plugin/tests/history.test.ts telegram-plugin/tests/cross-turn-card-gate.test.ts telegram-plugin/tests/emission-authority-open-gate.test.ts telegram-plugin/tests/emission-authority-ping-gate.test.ts telegram-plugin/tests/emission-authority-card-drain-gate.test.ts telegram-plugin/tests/per-topic-current-turn.test.ts telegram-plugin/tests/history-reaper.test.ts telegram-plugin/tests/ipc-server-client.test.ts telegram-plugin/tests/ipc-server-race.test.ts telegram-plugin/tests/ipc-server-query-pending-permission.test.ts telegram-plugin/tests/ipc-server-check-pre-approved.test.ts telegram-plugin/tests/rollout-narration-edit-socket.test.ts telegram-plugin/tests/gateway-bridge.test.ts telegram-plugin/tests/gateway-startup-mutex.test.ts telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts telegram-plugin/tests/boot-card-dedupe.test.ts telegram-plugin/tests/boot-card-reason.test.ts telegram-plugin/tests/progress-update.test.ts telegram-plugin/tests/quota-cache.test.ts telegram-plugin/tests/unhandled-rejection-policy.test.ts telegram-plugin/tests/registry-turns.test.ts telegram-plugin/registry/subagents.test.ts telegram-plugin/registry/subagents-bugs.test.ts telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts telegram-plugin/tests/subagent-nested-dispatch.test.ts telegram-plugin/tests/nested-worker-visibility-harness.test.ts telegram-plugin/tests/turns-writer.test.ts telegram-plugin/tests/resume-inbound-builder.test.ts telegram-plugin/registry/api-registry.test.ts telegram-plugin/registry/turns-schema.test.ts telegram-plugin/tests/idle-footer-wiring.test.ts telegram-plugin/tests/subagent-tracker-hooks.test.ts telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts telegram-plugin/tests/status-query-telemetry.test.ts telegram-plugin/tests/reaction-trigger.test.ts telegram-plugin/tests/reaction-trigger-flow.test.ts telegram-plugin/gateway/webhook-ingest-server.test.ts telegram-plugin/tests/skill-proposal-card.test.ts",
|
|
27
|
+
"test": "vitest run && bun test telegram-plugin/tests/agent-state-dir-preload.test.ts telegram-plugin/tests/catch-all-forwarded-history.test.ts telegram-plugin/tests/history.test.ts telegram-plugin/tests/boot-briefing-builder.test.ts telegram-plugin/tests/cross-turn-card-gate.test.ts telegram-plugin/tests/emission-authority-open-gate.test.ts telegram-plugin/tests/emission-authority-ping-gate.test.ts telegram-plugin/tests/emission-authority-card-drain-gate.test.ts telegram-plugin/tests/per-topic-current-turn.test.ts telegram-plugin/tests/history-reaper.test.ts telegram-plugin/tests/ipc-server-client.test.ts telegram-plugin/tests/ipc-server-race.test.ts telegram-plugin/tests/ipc-server-buzz-dedup.test.ts telegram-plugin/tests/ipc-server-query-pending-permission.test.ts telegram-plugin/tests/ipc-server-check-pre-approved.test.ts telegram-plugin/tests/rollout-narration-edit-socket.test.ts telegram-plugin/tests/gateway-bridge.test.ts telegram-plugin/tests/gateway-startup-mutex.test.ts telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts telegram-plugin/tests/boot-card-dedupe.test.ts telegram-plugin/tests/boot-card-reason.test.ts telegram-plugin/tests/progress-update.test.ts telegram-plugin/tests/quota-cache.test.ts telegram-plugin/tests/unhandled-rejection-policy.test.ts telegram-plugin/tests/registry-turns.test.ts telegram-plugin/registry/subagents.test.ts telegram-plugin/registry/subagents-bugs.test.ts telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts telegram-plugin/tests/subagent-nested-dispatch.test.ts telegram-plugin/tests/nested-worker-visibility-harness.test.ts telegram-plugin/tests/turns-writer.test.ts telegram-plugin/tests/resume-inbound-builder.test.ts telegram-plugin/registry/api-registry.test.ts telegram-plugin/registry/turns-schema.test.ts telegram-plugin/tests/idle-footer-wiring.test.ts telegram-plugin/tests/subagent-tracker-hooks.test.ts telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts telegram-plugin/tests/status-query-telemetry.test.ts telegram-plugin/tests/reaction-trigger.test.ts telegram-plugin/tests/reaction-trigger-flow.test.ts telegram-plugin/gateway/webhook-ingest-server.test.ts telegram-plugin/tests/skill-proposal-card.test.ts",
|
|
28
28
|
"test:vitest": "vitest run",
|
|
29
|
-
"test:bun": "bun test telegram-plugin/tests/agent-state-dir-preload.test.ts telegram-plugin/tests/catch-all-forwarded-history.test.ts src/vault/grants.test.ts src/vault/grants-db.test.ts src/vault/write-grants.test.ts src/vault/broker/server-grants.test.ts src/vault/broker/server-write-grants.test.ts src/vault/broker/server-scope-persist.test.ts src/vault/broker/server-tokenless-scope.test.ts src/vault/broker/server-mint-grant-passphrase-attest.test.ts src/vault/broker/server-passphrase-attest.test.ts src/vault/broker/server-mint-grant-posture-attest.test.ts src/vault/broker/server-admin-only-keys.test.ts src/vault/broker/client-token.test.ts src/vault/broker/server-unlock.test.ts src/vault/broker/auto-unlock.test.ts src/vault/broker/drift-detection.test.ts tests/vault-broker-passphrase.test.ts src/cli/vault-get-broker.test.ts src/vault/resolver-via-broker.test.ts src/vault/broker/scope.test.ts src/vault/broker/server.test.ts src/litellm/provision-apply-e2e.test.ts src/drive/disconnect.test.ts src/drive/grants.test.ts src/drive/oauth.test.ts src/drive/onboarding.test.ts src/drive/reconciler.test.ts src/drive/vault-slots.test.ts src/drive/wrapper.test.ts src/vault/approvals/kernel.test.ts src/vault/approvals/approval-origin.test.ts src/vault/approvals/self-approval-bypass.test.ts src/vault/approvals/schema-idempotent.test.ts src/vault/broker/server-approvals.test.ts telegram-plugin/tests/boot-probes.test.ts telegram-plugin/tests/boot-version-string.test.ts telegram-plugin/tests/history.test.ts telegram-plugin/tests/cross-turn-card-gate.test.ts telegram-plugin/tests/emission-authority-open-gate.test.ts telegram-plugin/tests/emission-authority-ping-gate.test.ts telegram-plugin/tests/emission-authority-card-drain-gate.test.ts telegram-plugin/tests/per-topic-current-turn.test.ts telegram-plugin/tests/history-reaper.test.ts telegram-plugin/tests/ipc-server-client.test.ts telegram-plugin/tests/ipc-server-race.test.ts telegram-plugin/tests/ipc-server-query-pending-permission.test.ts telegram-plugin/tests/ipc-server-check-pre-approved.test.ts telegram-plugin/tests/rollout-narration-edit-socket.test.ts telegram-plugin/tests/gateway-bridge.test.ts telegram-plugin/tests/gateway-startup-mutex.test.ts telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts telegram-plugin/tests/boot-card-dedupe.test.ts telegram-plugin/tests/boot-card-reason.test.ts telegram-plugin/tests/progress-update.test.ts telegram-plugin/tests/quota-cache.test.ts telegram-plugin/tests/silent-reply-guard.test.ts telegram-plugin/tests/unhandled-rejection-policy.test.ts telegram-plugin/tests/registry-turns.test.ts telegram-plugin/registry/subagents.test.ts telegram-plugin/registry/subagents-bugs.test.ts telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts telegram-plugin/tests/subagent-nested-dispatch.test.ts telegram-plugin/tests/nested-worker-visibility-harness.test.ts telegram-plugin/tests/turns-writer.test.ts telegram-plugin/tests/resume-inbound-builder.test.ts telegram-plugin/tests/subagent-tracker-hooks.test.ts telegram-plugin/tests/resolve-calling-subagent.test.ts telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts telegram-plugin/tests/status-query-telemetry.test.ts telegram-plugin/tests/reaction-trigger.test.ts telegram-plugin/tests/reaction-trigger-flow.test.ts telegram-plugin/tests/subagent-watcher-workflow-visibility.test.ts telegram-plugin/uat/load-env.test.ts telegram-plugin/uat/feed-matcher.test.ts telegram-plugin/uat/uat-driver.test.ts telegram-plugin/gateway/webhook-ingest-server.test.ts telegram-plugin/tests/skill-proposal-card.test.ts",
|
|
29
|
+
"test:bun": "bun test telegram-plugin/tests/agent-state-dir-preload.test.ts telegram-plugin/tests/catch-all-forwarded-history.test.ts src/vault/grants.test.ts src/vault/grants-db.test.ts src/vault/write-grants.test.ts src/vault/broker/server-grants.test.ts src/vault/broker/server-write-grants.test.ts src/vault/broker/server-scope-persist.test.ts src/vault/broker/server-tokenless-scope.test.ts src/vault/broker/server-mint-grant-passphrase-attest.test.ts src/vault/broker/server-passphrase-attest.test.ts src/vault/broker/server-mint-grant-posture-attest.test.ts src/vault/broker/server-admin-only-keys.test.ts src/vault/broker/client-token.test.ts src/vault/broker/server-unlock.test.ts src/vault/broker/auto-unlock.test.ts src/vault/broker/drift-detection.test.ts tests/vault-broker-passphrase.test.ts src/cli/vault-get-broker.test.ts src/vault/resolver-via-broker.test.ts src/vault/broker/scope.test.ts src/vault/broker/server.test.ts src/litellm/provision-apply-e2e.test.ts src/drive/disconnect.test.ts src/drive/grants.test.ts src/drive/oauth.test.ts src/drive/onboarding.test.ts src/drive/reconciler.test.ts src/drive/vault-slots.test.ts src/drive/wrapper.test.ts src/vault/approvals/kernel.test.ts src/vault/approvals/approval-origin.test.ts src/vault/approvals/self-approval-bypass.test.ts src/vault/approvals/schema-idempotent.test.ts src/vault/broker/server-approvals.test.ts telegram-plugin/tests/boot-probes.test.ts telegram-plugin/tests/boot-version-string.test.ts telegram-plugin/tests/history.test.ts telegram-plugin/tests/boot-briefing-builder.test.ts telegram-plugin/tests/cross-turn-card-gate.test.ts telegram-plugin/tests/emission-authority-open-gate.test.ts telegram-plugin/tests/emission-authority-ping-gate.test.ts telegram-plugin/tests/emission-authority-card-drain-gate.test.ts telegram-plugin/tests/per-topic-current-turn.test.ts telegram-plugin/tests/history-reaper.test.ts telegram-plugin/tests/ipc-server-client.test.ts telegram-plugin/tests/ipc-server-race.test.ts telegram-plugin/tests/ipc-server-buzz-dedup.test.ts telegram-plugin/tests/ipc-server-query-pending-permission.test.ts telegram-plugin/tests/ipc-server-check-pre-approved.test.ts telegram-plugin/tests/rollout-narration-edit-socket.test.ts telegram-plugin/tests/gateway-bridge.test.ts telegram-plugin/tests/gateway-startup-mutex.test.ts telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts telegram-plugin/tests/boot-card-dedupe.test.ts telegram-plugin/tests/boot-card-reason.test.ts telegram-plugin/tests/progress-update.test.ts telegram-plugin/tests/quota-cache.test.ts telegram-plugin/tests/silent-reply-guard.test.ts telegram-plugin/tests/unhandled-rejection-policy.test.ts telegram-plugin/tests/registry-turns.test.ts telegram-plugin/registry/subagents.test.ts telegram-plugin/registry/subagents-bugs.test.ts telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts telegram-plugin/tests/subagent-nested-dispatch.test.ts telegram-plugin/tests/nested-worker-visibility-harness.test.ts telegram-plugin/tests/turns-writer.test.ts telegram-plugin/tests/resume-inbound-builder.test.ts telegram-plugin/tests/subagent-tracker-hooks.test.ts telegram-plugin/tests/resolve-calling-subagent.test.ts telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts telegram-plugin/tests/status-query-telemetry.test.ts telegram-plugin/tests/reaction-trigger.test.ts telegram-plugin/tests/reaction-trigger-flow.test.ts telegram-plugin/tests/subagent-watcher-workflow-visibility.test.ts telegram-plugin/uat/load-env.test.ts telegram-plugin/uat/feed-matcher.test.ts telegram-plugin/uat/uat-driver.test.ts telegram-plugin/gateway/webhook-ingest-server.test.ts telegram-plugin/tests/skill-proposal-card.test.ts",
|
|
30
30
|
"test:watch": "vitest",
|
|
31
31
|
"lint": "tsc --noEmit && node scripts/check-plugin-references.mjs && bash scripts/check-bot-api-wrapping.sh && node scripts/check-bun-test-imports.mjs && node scripts/check-test-runner-coverage.mjs && node scripts/check-bun-module-mock-scope.mjs && node scripts/check-no-pii-secrets.mjs && node scripts/check-vault-test-hermeticity.mjs && node scripts/check-auth-test-hermeticity.mjs && node scripts/check-agent-state-dir-hermeticity.mjs && node scripts/check-no-broadcast-delivery.mjs && node scripts/check-stale-tool-descriptions.mjs && node scripts/check-mcp-instructions-budget.mjs && node scripts/check-web-subscription-honest.mjs && node scripts/check-no-unpinned-npx-playwright.mjs && node scripts/check-gateway-line-ratchet.mjs && node scripts/check-retry-flood-hooks.mjs && node scripts/check-callback-ctx-wrapping.mjs && node scripts/check-status-pin-single-path.mjs && node scripts/check-litellm-config-guard.mjs && node scripts/check-release-asset-names.mjs && node scripts/check-agent-attribution-trailers.mjs && node scripts/check-hindsight-write-redaction.mjs && bun scripts/check-secret-pattern-parity.ts",
|
|
32
32
|
"lint:tsc": "tsc --noEmit",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"commander": "^13.1.0",
|
|
64
64
|
"grammy": "^1.44",
|
|
65
65
|
"handlebars": "^4.7.8",
|
|
66
|
+
"nostr-tools": "^2.24.1",
|
|
66
67
|
"posthog-node": "^5.29.2",
|
|
67
68
|
"yaml": "^2.7.0",
|
|
68
69
|
"zod": "^3.24.0"
|
|
@@ -57,6 +57,48 @@ if [ "$SWITCHROOM_RUNTIME" = "docker" ] && [ -z "$SWITCHROOM_DOCKER_TMUX_INNER"
|
|
|
57
57
|
# `claude` --continue path, not the gateway), so this needs its own hoist.
|
|
58
58
|
export SWITCHROOM_BOOT_RESUME="{{#if bootResumeMode}}{{{bootResumeMode}}}{{else}}in-flight{{/if}}"
|
|
59
59
|
|
|
60
|
+
# Boot-briefing transport flag (session_continuity.briefing; default
|
|
61
|
+
# 'legacy'). When 'gateway', the gateway daemon assembles the fresh-session
|
|
62
|
+
# reorientation briefing at boot from the durable history.db and injects it
|
|
63
|
+
# as a spooled synthetic inbound (<channel source="boot_briefing">) instead
|
|
64
|
+
# of the handoff-briefing.sh --append-system-prompt path. MUST be exported
|
|
65
|
+
# here, before the gateway fork below (same env-fork landmine as
|
|
66
|
+
# SWITCHROOM_BOOT_RESUME). The gateway also needs the resume MODE to
|
|
67
|
+
# suppress the briefing when 'continue'/'auto' may replay the transcript —
|
|
68
|
+
# hoist that too (the inner-pass SWITCHROOM_RESUME_MODE assignment below
|
|
69
|
+
# renders the same template value, so the two can never disagree).
|
|
70
|
+
export SWITCHROOM_SESSION_BRIEFING="{{#if sessionBriefingMode}}{{{sessionBriefingMode}}}{{else}}legacy{{/if}}"
|
|
71
|
+
export SWITCHROOM_RESUME_MODE="{{#if resumeMode}}{{{resumeMode}}}{{else}}handoff{{/if}}"
|
|
72
|
+
|
|
73
|
+
# Force-fresh suppression signal for the gateway boot briefing. The gateway
|
|
74
|
+
# suppresses the briefing when this boot is a /reset·/new fresh start —
|
|
75
|
+
# re-feeding the just-reset conversation into the fresh session would defeat
|
|
76
|
+
# the reset. It MUST NOT rely on existsSync(.force-fresh-session) at its own
|
|
77
|
+
# module-eval time: this OUTER pass forks the gateway with `&` (below) then
|
|
78
|
+
# execs the inner tmux pass, whose CONTINUE_FLAG block `rm`s the marker (see
|
|
79
|
+
# ~line 1262). The fork and the `rm` race with no ordering, and under boot
|
|
80
|
+
# CPU contention the `rm` can win before the gateway bundle evaluates its
|
|
81
|
+
# briefing check — the marker vanishes and the reset session gets re-fed the
|
|
82
|
+
# conversation it just reset. Snapshot the marker into env HERE, before the
|
|
83
|
+
# fork, so the gateway reads a value fixed at fork time and immune to the
|
|
84
|
+
# later `rm`. The inner pass still consumes the marker for the claude
|
|
85
|
+
# --continue / session-mode logic; this only mirrors it for the fork race.
|
|
86
|
+
if [ -f "{{agentDir}}/.force-fresh-session" ]; then
|
|
87
|
+
export SWITCHROOM_FORCE_FRESH=1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Session-generation stamp for the gateway boot briefing (#4242). Set ONCE
|
|
91
|
+
# here, per real boot, before the gateway fork below. `_switchroom_supervise`
|
|
92
|
+
# respawns the gateway `bun` process in a `while` loop inside this same shell
|
|
93
|
+
# (it does NOT re-run start.sh), so every crash-respawn inherits this exact
|
|
94
|
+
# value, while the next real container boot re-derives a fresh one. The
|
|
95
|
+
# gateway persists it the first time it briefs and skips re-minting when the
|
|
96
|
+
# persisted stamp matches — that distinguishes "supervisor respawned me into
|
|
97
|
+
# a still-live Claude session" (skip) from "genuine new boot" (brief). The
|
|
98
|
+
# epoch second keeps it monotonic across boots; $$ (this outer pass's PID)
|
|
99
|
+
# plus ${RANDOM} guarantee uniqueness even for two boots within one second.
|
|
100
|
+
export SWITCHROOM_GATEWAY_BOOT_ID="$(date +%s)-$$-${RANDOM}"
|
|
101
|
+
|
|
60
102
|
# Gateway-consumed env MUST be exported HERE, before the gateway fork
|
|
61
103
|
# below. The gateway daemon reads channels.telegram.* knobs (and any
|
|
62
104
|
# agent env) from process.env at startup — e.g. SWITCHROOM_TG_STREAM_
|
|
@@ -455,6 +497,34 @@ x-litellm-tags: agent:$SWITCHROOM_AGENT_NAME,profile:${SWITCHROOM_AGENT_PROFILE:
|
|
|
455
497
|
bun /opt/switchroom/agent-scheduler/index.js &
|
|
456
498
|
fi
|
|
457
499
|
|
|
500
|
+
# 3b) Buzz co-channel inbound sidecar (Phase 1, channels.buzz).
|
|
501
|
+
# A supervised sibling that opens a WebSocket Nostr subscription to a
|
|
502
|
+
# closed Buzz relay, NIP-42-authenticates, and injects allowlisted
|
|
503
|
+
# messages onto the gateway socket as synthesized turns
|
|
504
|
+
# (meta.source="buzz") — exactly like the cron sidecar above. INBOUND
|
|
505
|
+
# ONLY in Phase 1; replies land on Telegram (the authoritative surface).
|
|
506
|
+
#
|
|
507
|
+
# Default OFF and deterministic: the block is present for every agent
|
|
508
|
+
# (start.sh stays byte-identical fleet-wide) but forks the sidecar ONLY
|
|
509
|
+
# when BUZZ_ENABLED=1 is set in the container env. NOTE: nothing in this
|
|
510
|
+
# branch sets BUZZ_ENABLED (or the other BUZZ_* vars) — projecting an
|
|
511
|
+
# ENABLED channels.buzz block into container env is DEFERRED to the
|
|
512
|
+
# deploy-wiring phase. Until then the env is unset for every agent and
|
|
513
|
+
# this block is a no-op, so the channel is dark by construction. The
|
|
514
|
+
# sidecar itself also fails closed if its env is incomplete, and fetches
|
|
515
|
+
# the nsec in-process from the vault broker (never an env var, never
|
|
516
|
+
# logged).
|
|
517
|
+
if [ "$BUZZ_ENABLED" = "1" ] \
|
|
518
|
+
&& [ -f /opt/switchroom/buzz-gateway/index.js ] \
|
|
519
|
+
&& command -v bun >/dev/null 2>&1; then
|
|
520
|
+
# --oneshot-ok: the sidecar exits 0 when the channel resolves not-live
|
|
521
|
+
# (e.g. mirror:off, or a not-live env). A clean exit 0 = "nothing to do",
|
|
522
|
+
# so DON'T respawn it every 60s (that is the documented churn pattern
|
|
523
|
+
# above). A non-zero crash while live still respawns.
|
|
524
|
+
_switchroom_supervise buzz-gateway /var/log/switchroom/buzz-gateway.log --oneshot-ok \
|
|
525
|
+
bun /opt/switchroom/buzz-gateway/index.js &
|
|
526
|
+
fi
|
|
527
|
+
|
|
458
528
|
{{#if cronSessionEnabled}}
|
|
459
529
|
# 4) cheap cron SESSION (Tier 1, reference/rfcs/cheap-cron-sessions.md §2.2).
|
|
460
530
|
# A SECOND interactive claude (no -p) dedicated to context:fresh cron
|
|
@@ -1353,7 +1423,14 @@ if [ "$_HANDOFF_STALE" = "1" ]; then
|
|
|
1353
1423
|
# daily memory into a compact context brief. Requires python3 (stdlib
|
|
1354
1424
|
# sqlite3) for the Telegram history section; other sections only need
|
|
1355
1425
|
# curl + jq (both optional — each source degrades gracefully).
|
|
1356
|
-
|
|
1426
|
+
#
|
|
1427
|
+
# SKIPPED when session_continuity.briefing='gateway': the gateway daemon
|
|
1428
|
+
# then owns the reorientation briefing (built from the durable history.db
|
|
1429
|
+
# and injected as a spooled <channel source="boot_briefing"> inbound), so
|
|
1430
|
+
# running the shell assembler here would double-inject the same recent
|
|
1431
|
+
# conversation. The Stop-hook .handoff.md path below is left untouched in
|
|
1432
|
+
# this stage of the migration.
|
|
1433
|
+
if [ "$SWITCHROOM_RESUME_MODE" = "handoff" ] && [ "{{#if sessionBriefingMode}}{{{sessionBriefingMode}}}{{else}}legacy{{/if}}" != "gateway" ] && command -v handoff-briefing.sh >/dev/null 2>&1; then
|
|
1357
1434
|
export AGENT_DIR="{{agentDir}}"
|
|
1358
1435
|
timeout 5 handoff-briefing.sh 2>/dev/null || true
|
|
1359
1436
|
fi
|
|
@@ -50,7 +50,7 @@ Creating, updating, or refreshing a mental model is **operator-approved** — do
|
|
|
50
50
|
### Directives (replaces feedback rules)
|
|
51
51
|
Hard rules the agent must follow during reflect — guardrails that are always applied.
|
|
52
52
|
|
|
53
|
-
- `mcp__hindsight__create_directive(text)` — e.g., `create_directive("Always prefer TypeScript over JavaScript for this user's projects")`. When the user gives you a correction or "always do X" rule, create a directive instead of writing a feedback `.md` file.
|
|
53
|
+
- `mcp__hindsight__create_directive(text)` — e.g., `create_directive("Always prefer TypeScript over JavaScript for this user's projects")`. When the user gives you a correction or "always do X" rule, create a directive instead of writing a feedback `.md` file. If the rule can be enforced deterministically — a settings.json hook, a permission rule, a skill/script edit, or a config change — prefer that (instead of, or in addition to, the directive) and say which you did; reserve directives for judgment rules code can't enforce.
|
|
54
54
|
|
|
55
55
|
(Read-only inspection tools like `list_memories`, `list_mental_models`, `get_mental_model`, `list_directives` are available under the `mcp__hindsight__*` namespace if you ever need them, but you rarely should — Hindsight's own auto-recall surfaces what matters and the operator handles bank curation out-of-band. The mental-model WRITE tools — `create`/`update`/`refresh`/`delete_mental_model` — are gated: propose via `mcp__switchroom-telegram__mental_model_propose`, don't call them directly.)
|
|
56
56
|
|
|
@@ -18,6 +18,32 @@ judgement criteria. This skill carries the parts that are *this fleet's
|
|
|
18
18
|
specific opinion* — the ones you would get wrong by defaulting to generic
|
|
19
19
|
good practice, because our answer differs from the obvious one.
|
|
20
20
|
|
|
21
|
+
## 0. The design contract binds first (repos that declare one)
|
|
22
|
+
|
|
23
|
+
Switchroom declares its design contract in `reference/` (`reference/README.md`
|
|
24
|
+
is the map; the repo CLAUDE.md "Design contract" section binds it to every
|
|
25
|
+
PR). When the repo you are changing carries one, the whole protocol below
|
|
26
|
+
runs *inside* that contract:
|
|
27
|
+
|
|
28
|
+
- **Before non-trivial work, place the change in the contract:** which of the
|
|
29
|
+
four outcomes it advances, and which job spec it satisfies —
|
|
30
|
+
`reference/product-spec.md` owns the job index; survey specs cheaply with
|
|
31
|
+
`head -7 reference/jobs/*.md`. A change that maps to no outcome and no job
|
|
32
|
+
is a scoping question, not a coding task.
|
|
33
|
+
- **The ship gate is the verdict rule, not just CI green:** a change ships
|
|
34
|
+
only when it (a) advances one of the four outcomes, (b) satisfies its job
|
|
35
|
+
spec — proven by that job's outcome UAT, (c) passes all three principle
|
|
36
|
+
checks in `reference/principles.md` (docs / defaults / consistency; a "no"
|
|
37
|
+
is a redesign, not a ship-and-patch), and (d) crosses no invariant in
|
|
38
|
+
`reference/invariants.md`. Cite the job spec in the PR body.
|
|
39
|
+
- **Design lives in `reference/`, not `docs/`.** A design decision worth
|
|
40
|
+
recording is an RFC or design record in `reference/rfcs/` (`serves:` a job
|
|
41
|
+
or `backs:` an invariant), never a new doc under `docs/`.
|
|
42
|
+
|
|
43
|
+
This does not add ceremony to small changes: §1's fast path stands, and on a
|
|
44
|
+
single-concern change the verdict check is a one-line sanity pass, not a
|
|
45
|
+
report.
|
|
46
|
+
|
|
21
47
|
## 1. Is this a "larger" task? (decides whether you design-align first)
|
|
22
48
|
|
|
23
49
|
Treat it as larger — design report before implementing — when ANY of these hold:
|
|
@@ -37,7 +63,10 @@ Design-aligning a one-liner is its own failure mode.
|
|
|
37
63
|
|
|
38
64
|
The report states what exists today **with citations**, what will change, the
|
|
39
65
|
chosen approach, the alternatives you rejected and why, and the PR staging
|
|
40
|
-
plan.
|
|
66
|
+
plan. In a contract-carrying repo (§0) it opens with the verdict-rule
|
|
67
|
+
mapping: the outcome advanced, the job spec satisfied, and any principle
|
|
68
|
+
check or invariant the design brushes against. Get alignment before
|
|
69
|
+
implementing.
|
|
41
70
|
|
|
42
71
|
Then red-team your own plan item by item. Each item gets a verdict —
|
|
43
72
|
`SOUND`, `RISK`, or `WRONG` — backed by evidence you can point at (a file you
|
|
@@ -59,3 +59,8 @@ Switchroom is a multi-agent orchestrator built on Claude Code. It manages multip
|
|
|
59
59
|
- [cascade.md](cascade.md) — three-layer config cascade semantics
|
|
60
60
|
- [sub-agents.md](sub-agents.md) — delegation patterns and model routing
|
|
61
61
|
- [telegram.md](telegram.md) — enhanced Telegram plugin features
|
|
62
|
+
|
|
63
|
+
**Why it's built this way** lives in the repo's `reference/` directory — the
|
|
64
|
+
design contract (`reference/README.md` is the map: vision, principles,
|
|
65
|
+
invariants, product spec, job specs, RFCs). For "why does switchroom do X"
|
|
66
|
+
questions, that is the source of truth; `docs/` is usage/operation only.
|
|
@@ -290,7 +290,7 @@ config-plus-recreate operation with three known traps. **Follow the runbook:
|
|
|
290
290
|
|
|
291
291
|
## Telegram plugin reference — "what MCP tools", "how does reply work"
|
|
292
292
|
|
|
293
|
-
The `switchroom-telegram` plugin is an enhanced fork of the official Telegram MCP plugin and is the default for all switchroom agents. It exposes **
|
|
293
|
+
The `switchroom-telegram` plugin is an enhanced fork of the official Telegram MCP plugin and is the default for all switchroom agents. It exposes **20+ MCP tools** (all prefixed `mcp__switchroom-telegram__`; the authoritative list is the tool declarations in `telegram-plugin/bridge/bridge.ts`). The core messaging set:
|
|
294
294
|
|
|
295
295
|
| Tool | Purpose |
|
|
296
296
|
|---|---|
|
|
@@ -23121,12 +23121,12 @@ function classifyClaudeError(raw) {
|
|
|
23121
23121
|
try {
|
|
23122
23122
|
return classifyInner(raw);
|
|
23123
23123
|
} catch {
|
|
23124
|
-
return "unknown-
|
|
23124
|
+
return "unknown-5xx";
|
|
23125
23125
|
}
|
|
23126
23126
|
}
|
|
23127
23127
|
function classifyInner(raw) {
|
|
23128
23128
|
if (raw == null)
|
|
23129
|
-
return "unknown-
|
|
23129
|
+
return "unknown-5xx";
|
|
23130
23130
|
const obj = typeof raw === "object" ? raw : {};
|
|
23131
23131
|
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
23132
23132
|
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
@@ -23171,13 +23171,16 @@ ${message}`;
|
|
|
23171
23171
|
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
23172
23172
|
return "agent-restarted-unexpectedly";
|
|
23173
23173
|
}
|
|
23174
|
+
if ((status == null || status >= 500) && (errorType === "server_error" || errorCode === "server_error" || sdkCode === "server_error" || errorType === "api_error" || errorCode === "api_error" || sdkCode === "api_error")) {
|
|
23175
|
+
return "transport-transient";
|
|
23176
|
+
}
|
|
23174
23177
|
if (status != null) {
|
|
23175
23178
|
if (status >= 400 && status < 500)
|
|
23176
23179
|
return "unknown-4xx";
|
|
23177
23180
|
if (status >= 500 && status < 600)
|
|
23178
23181
|
return "unknown-5xx";
|
|
23179
23182
|
}
|
|
23180
|
-
return "unknown-
|
|
23183
|
+
return "unknown-5xx";
|
|
23181
23184
|
}
|
|
23182
23185
|
function extractString(obj, key) {
|
|
23183
23186
|
const v = obj[key];
|
|
@@ -23708,7 +23711,7 @@ ${errStr}`) ? "rate-limited" : "quota-exhausted" : classifyClaudeError({ type: e
|
|
|
23708
23711
|
const raw = embeddedError ?? obj;
|
|
23709
23712
|
const kind = classifyClaudeError(embeddedError ?? obj);
|
|
23710
23713
|
const detail = extractDetailMessage(embeddedError) ?? extractDetailMessage(obj) ?? String(type ?? "");
|
|
23711
|
-
const transient = kind === "rate-limited";
|
|
23714
|
+
const transient = kind === "rate-limited" || kind === "transport-transient";
|
|
23712
23715
|
const retry = extractRetryState(obj);
|
|
23713
23716
|
const terminal = !transient ? true : retry.retryAttempt != null && retry.maxRetries != null ? retry.retryAttempt >= retry.maxRetries : isErrorLine;
|
|
23714
23717
|
return { kind, raw, detail, transient, terminal };
|