switchroom 0.7.15 → 0.10.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/README.md +51 -59
- package/bin/run-hook.sh +27 -11
- package/bin/timezone-hook.sh +9 -7
- package/dist/agent-scheduler/index.js +410 -133
- package/dist/auth-broker/index.js +13932 -0
- package/dist/cli/switchroom.js +26937 -5601
- package/dist/host-control/main.js +12702 -0
- package/dist/vault/approvals/kernel-server.js +467 -184
- package/dist/vault/broker/server.js +1430 -724
- package/examples/minimal.yaml +63 -0
- package/examples/personal-google-workspace-mcp/.env.example +34 -0
- package/examples/personal-google-workspace-mcp/README.md +194 -0
- package/examples/personal-google-workspace-mcp/compose.yaml +66 -0
- package/examples/switchroom.yaml +220 -0
- package/package.json +7 -4
- package/profiles/_base/settings.json.hbs +20 -5
- package/profiles/_base/start.sh.hbs +16 -3
- package/profiles/_shared/agent-self-service.md.hbs +126 -0
- package/profiles/_shared/telegram-style.md.hbs +20 -90
- package/profiles/_shared/vault-protocol.md.hbs +68 -0
- package/profiles/default/CLAUDE.md +50 -96
- package/profiles/default/CLAUDE.md.hbs +36 -6
- package/profiles/default/workspace/SOUL.md.hbs +12 -5
- package/skills/buildkite-agent-infrastructure/SKILL.md +30 -11
- package/skills/buildkite-agent-runtime/SKILL.md +44 -11
- package/skills/buildkite-api/SKILL.md +31 -8
- package/skills/buildkite-cli/SKILL.md +27 -9
- package/skills/buildkite-migration/SKILL.md +22 -9
- package/skills/buildkite-pipelines/SKILL.md +26 -9
- package/skills/buildkite-secure-delivery/SKILL.md +23 -9
- package/skills/buildkite-test-engine/SKILL.md +25 -8
- package/skills/docx/SKILL.md +1 -1
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/file-bug/SKILL.md +34 -6
- package/skills/humanizer/SKILL.md +15 -0
- package/skills/humanizer-calibrate/SKILL.md +7 -1
- package/skills/mcp-builder/SKILL.md +1 -1
- package/skills/pdf/SKILL.md +1 -1
- package/skills/pptx/SKILL.md +1 -1
- package/skills/skill-creator/SKILL.md +21 -1
- package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/skills/switchroom-cli/SKILL.md +63 -64
- package/skills/switchroom-health/SKILL.md +23 -10
- package/skills/switchroom-install/SKILL.md +3 -3
- package/skills/switchroom-manage/SKILL.md +26 -19
- package/skills/switchroom-runtime/SKILL.md +191 -0
- package/skills/switchroom-status/SKILL.md +27 -2
- package/skills/telegram-test-harness/SKILL.md +3 -0
- package/skills/token-helpers/SKILL.md +24 -1
- package/skills/webapp-testing/SKILL.md +31 -1
- package/skills/xlsx/SKILL.md +1 -1
- package/telegram-plugin/admin-commands/index.ts +7 -5
- package/telegram-plugin/analytics-posthog.ts +191 -0
- package/telegram-plugin/bridge/bridge.ts +69 -0
- package/telegram-plugin/bridge/ipc-client.ts +4 -1
- package/telegram-plugin/dist/bridge/bridge.js +194 -119
- package/telegram-plugin/dist/gateway/gateway.js +23611 -19671
- package/telegram-plugin/dist/server.js +245 -189
- package/telegram-plugin/first-paint.ts +3 -24
- package/telegram-plugin/gateway/auth-add-flow.ts +326 -0
- package/telegram-plugin/gateway/auth-broker-client.ts +75 -0
- package/telegram-plugin/gateway/auth-command.ts +794 -0
- package/telegram-plugin/gateway/auth-line.ts +123 -0
- package/telegram-plugin/gateway/boot-card.ts +169 -40
- package/telegram-plugin/gateway/boot-issue-cache.ts +308 -0
- package/telegram-plugin/gateway/boot-probes.ts +166 -123
- package/telegram-plugin/gateway/boot-reason.ts +41 -7
- package/telegram-plugin/gateway/boot-version.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +3499 -1885
- package/telegram-plugin/gateway/hostd-dispatch.ts +117 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +18 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +106 -0
- package/telegram-plugin/gateway/quarantine.ts +69 -0
- package/telegram-plugin/gateway/quota-cache.ts +9 -4
- package/telegram-plugin/gateway/reaction-trigger.ts +401 -0
- package/telegram-plugin/gateway/recent-denials.test.ts +103 -0
- package/telegram-plugin/gateway/recent-denials.ts +77 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +109 -31
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +125 -0
- package/telegram-plugin/history.ts +91 -0
- package/telegram-plugin/hooks/hooks.json +10 -0
- package/telegram-plugin/hooks/sandbox-hint-posttool.mjs +130 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +19 -2
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +22 -2
- package/telegram-plugin/hooks/tool-label-pretool.mjs +11 -0
- package/telegram-plugin/hooks/wedge-detect-posttool.mjs +303 -0
- package/telegram-plugin/inbound-classifier.ts +50 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +136 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/package.json +4 -2
- package/telegram-plugin/permission-rule.ts +51 -0
- package/telegram-plugin/permission-title.ts +56 -0
- package/telegram-plugin/quota-check.ts +19 -41
- package/telegram-plugin/registry/reaper.ts +223 -0
- package/telegram-plugin/retry-api-call.ts +80 -0
- package/telegram-plugin/runtime-metrics.ts +177 -0
- package/telegram-plugin/scripts/build.mjs +0 -1
- package/telegram-plugin/secret-detect/index.ts +24 -0
- package/telegram-plugin/secret-detect/vault-error.test.ts +64 -12
- package/telegram-plugin/secret-detect/vault-error.ts +78 -11
- package/telegram-plugin/secret-detect/vault-write.ts +14 -2
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/session-tail.ts +6 -1
- package/telegram-plugin/shared/bot-runtime.ts +5 -4
- package/telegram-plugin/silence-poke.ts +420 -0
- package/telegram-plugin/silent-end.ts +174 -0
- package/telegram-plugin/stream-controller.ts +13 -0
- package/telegram-plugin/stream-reply-handler.ts +7 -0
- package/telegram-plugin/subagent-watcher.ts +213 -4
- package/telegram-plugin/tests/auth-add-flow.test.ts +559 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +8 -4
- package/telegram-plugin/tests/auth-command-vernacular.test.ts +531 -0
- package/telegram-plugin/tests/boot-card-issue-dedup.test.ts +247 -0
- package/telegram-plugin/tests/boot-card-reason-to-render.test.ts +182 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +65 -2
- package/telegram-plugin/tests/boot-card-render.test.ts +146 -0
- package/telegram-plugin/tests/boot-card-silent-on-operator.test.ts +103 -0
- package/telegram-plugin/tests/boot-probes.test.ts +216 -10
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +190 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +26 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +12 -3
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +104 -0
- package/telegram-plugin/tests/history-reaper.test.ts +378 -0
- package/telegram-plugin/tests/hostd-dispatch.test.ts +129 -0
- package/telegram-plugin/tests/inbound-classifier.test.ts +76 -0
- package/telegram-plugin/tests/inbound-message-types.test.ts +267 -0
- package/telegram-plugin/tests/issues-card.test.ts +49 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +132 -0
- package/telegram-plugin/tests/permission-rule.test.ts +80 -1
- package/telegram-plugin/tests/permission-title.test.ts +31 -0
- package/telegram-plugin/tests/quota-check.test.ts +5 -35
- package/telegram-plugin/tests/races.test.ts +179 -0
- package/telegram-plugin/tests/reaction-trigger-flow.test.ts +353 -0
- package/telegram-plugin/tests/reaction-trigger.test.ts +397 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +152 -1
- package/telegram-plugin/tests/runtime-metrics.test.ts +145 -0
- package/telegram-plugin/tests/sandbox-hint-posttool.test.ts +155 -0
- package/telegram-plugin/tests/secret-detect-delete-must-surface-failures.test.ts +133 -0
- package/telegram-plugin/tests/secret-detect-false-positives.test.ts +137 -0
- package/telegram-plugin/tests/silence-poke.test.ts +493 -0
- package/telegram-plugin/tests/silent-end.test.ts +206 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +107 -0
- package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +224 -0
- package/telegram-plugin/tests/subagent-watcher-stall-terminal.test.ts +316 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +263 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +81 -0
- package/telegram-plugin/tests/vault-approval-posture.test.ts +256 -0
- package/telegram-plugin/tests/vault-grant-auto-resume.test.ts +73 -0
- package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +226 -0
- package/telegram-plugin/tests/vault-grant-union.test.ts +130 -0
- package/telegram-plugin/tests/vault-key-regex-allows-slash.test.ts +140 -0
- package/telegram-plugin/tests/vault-posture-quarantine.test.ts +104 -0
- package/telegram-plugin/tests/vault-request-access-tool.test.ts +114 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +106 -0
- package/telegram-plugin/turn-signal-tracker.ts +100 -24
- package/telegram-plugin/uat/SETUP.md +210 -35
- package/telegram-plugin/uat/assertions.ts +264 -37
- package/telegram-plugin/uat/driver-info.ts +57 -0
- package/telegram-plugin/uat/driver.ts +590 -51
- package/telegram-plugin/uat/harness.ts +140 -94
- package/telegram-plugin/uat/load-env.test.ts +72 -0
- package/telegram-plugin/uat/load-env.ts +48 -0
- package/telegram-plugin/uat/login.ts +96 -53
- package/telegram-plugin/uat/runners/agent-self-sufficiency.ts +457 -0
- package/telegram-plugin/uat/runners/paraphrases.ts +231 -0
- package/telegram-plugin/uat/runners/report.ts +150 -0
- package/telegram-plugin/uat/runners/run-agent-self-sufficiency.sh +50 -0
- package/telegram-plugin/uat/runners/scorer.test.ts +196 -0
- package/telegram-plugin/uat/runners/scorer.ts +106 -0
- package/telegram-plugin/uat/runners/skill-coverage.test.ts +100 -0
- package/telegram-plugin/uat/runners/skill-coverage.ts +620 -0
- package/telegram-plugin/uat/scenarios/ask-user-button-tap-dm.test.ts +141 -0
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +255 -0
- package/telegram-plugin/uat/scenarios/fuzz-human-style-dm.test.ts +275 -0
- package/telegram-plugin/uat/scenarios/fuzz-random-prompts-dm.test.ts +146 -0
- package/telegram-plugin/uat/scenarios/fuzz-status-ask-dm.test.ts +486 -0
- package/telegram-plugin/uat/scenarios/jtbd-interrupt-marker-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +100 -0
- package/telegram-plugin/uat/scenarios/jtbd-soft-commit-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-status-query-dm.test.ts +49 -0
- package/telegram-plugin/uat/scenarios/location-inbound-dm.test.ts +65 -0
- package/telegram-plugin/uat/scenarios/midturn-silent-dm.test.ts +175 -0
- package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +142 -0
- package/telegram-plugin/uat/scenarios/reactions-trigger-turn-dm.test.ts +96 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-deletes-original-dm.test.ts +123 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-no-false-positive-dm.test.ts +87 -0
- package/telegram-plugin/uat/scenarios/silence-poke-soft-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +95 -0
- package/telegram-plugin/uat/scenarios/smoke-dm-reply.test.ts +57 -0
- package/telegram-plugin/uat/scenarios/subagent-watcher-no-rerun-dm.test.ts +135 -0
- package/telegram-plugin/uat/scenarios/vault-approval-posture-telegram-id-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/vault-audit-allow-dm.test.ts +108 -0
- package/telegram-plugin/uat/scenarios/vault-grant-auto-resume-dm.test.ts +121 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-concurrent-dm.test.ts +161 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-end-to-end-dm.test.ts +158 -0
- package/telegram-plugin/uat/scenarios/voice-inbound-dm.test.ts +65 -0
- package/telegram-plugin/vault-approval-posture.ts +42 -0
- package/telegram-plugin/welcome-text.ts +1 -0
- package/telegram-plugin/active-pins-sweep.ts +0 -204
- package/telegram-plugin/active-pins.ts +0 -146
- package/telegram-plugin/auth-dashboard.ts +0 -1104
- package/telegram-plugin/auth-slot-parser.ts +0 -497
- package/telegram-plugin/card-event-log.ts +0 -138
- package/telegram-plugin/dist/foreman/foreman.js +0 -31106
- package/telegram-plugin/docs/multi-agent-card-design.md +0 -847
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +0 -144
- package/telegram-plugin/foreman/foreman-create-flow.ts +0 -202
- package/telegram-plugin/foreman/foreman-handlers.ts +0 -493
- package/telegram-plugin/foreman/foreman.ts +0 -1165
- package/telegram-plugin/foreman/setup-flow.ts +0 -345
- package/telegram-plugin/foreman/setup-state.ts +0 -239
- package/telegram-plugin/foreman/state.ts +0 -203
- package/telegram-plugin/pin-event-log.ts +0 -76
- package/telegram-plugin/progress-card-driver.ts +0 -2886
- package/telegram-plugin/progress-card-pin-manager.ts +0 -589
- package/telegram-plugin/progress-card-pin-watchdog.ts +0 -98
- package/telegram-plugin/progress-card.ts +0 -1409
- package/telegram-plugin/tests/HARNESS.md +0 -340
- package/telegram-plugin/tests/_progress-card-harness.ts +0 -109
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +0 -211
- package/telegram-plugin/tests/active-pins-sweep.test.ts +0 -309
- package/telegram-plugin/tests/active-pins.test.ts +0 -187
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +0 -118
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +0 -260
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +0 -140
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +0 -559
- package/telegram-plugin/tests/auth-dashboard.test.ts +0 -1045
- package/telegram-plugin/tests/auth-slot-commands.test.ts +0 -640
- package/telegram-plugin/tests/bg-agent-progress-card-757.test.ts +0 -201
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +0 -137
- package/telegram-plugin/tests/card-event-log.test.ts +0 -145
- package/telegram-plugin/tests/first-paint.test.ts +0 -257
- package/telegram-plugin/tests/foreman-create-flow.test.ts +0 -359
- package/telegram-plugin/tests/foreman-handlers.test.ts +0 -347
- package/telegram-plugin/tests/foreman-state.test.ts +0 -164
- package/telegram-plugin/tests/foreman-write-ops.test.ts +0 -214
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +0 -243
- package/telegram-plugin/tests/pin-event-log.test.ts +0 -124
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +0 -73
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +0 -272
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +0 -258
- package/telegram-plugin/tests/progress-card-delay-842.test.ts +0 -160
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +0 -81
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +0 -80
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +0 -215
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +0 -123
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +0 -76
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +0 -62
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +0 -84
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +0 -139
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +0 -773
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +0 -66
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +0 -64
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +0 -190
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +0 -146
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +0 -123
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +0 -82
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +0 -114
- package/telegram-plugin/tests/real-gateway-harness.ts +0 -699
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +0 -313
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +0 -299
- package/telegram-plugin/tests/real-gateway-spec.test.ts +0 -487
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +0 -101
- package/telegram-plugin/tests/setup-flow.test.ts +0 -510
- package/telegram-plugin/tests/setup-state.test.ts +0 -146
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +0 -116
- package/telegram-plugin/tests/turn-end-regressions.test.ts +0 -489
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +0 -218
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +0 -131
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +0 -120
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +0 -116
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +0 -87
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +0 -211
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +0 -62
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +0 -110
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +0 -128
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +0 -58
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +0 -133
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +0 -155
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +0 -117
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +0 -187
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +0 -149
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +0 -114
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +0 -105
- package/telegram-plugin/tests/waiting-ux-harness.ts +0 -381
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +0 -233
- package/telegram-plugin/turn-flush-prose-recovery.ts +0 -40
- package/telegram-plugin/two-zone-card.ts +0 -269
- package/telegram-plugin/uat/scenarios/smoke-clerk-reply.test.ts +0 -61
|
@@ -46,6 +46,19 @@ if [ "$SWITCHROOM_RUNTIME" = "docker" ] && [ -z "$SWITCHROOM_DOCKER_TMUX_INNER"
|
|
|
46
46
|
"$@" >> "$_logfile" 2>&1
|
|
47
47
|
local _exit=$?
|
|
48
48
|
local _now=$SECONDS
|
|
49
|
+
# Exit 78 = sysexits EX_CONFIG, the "permanent config error, do
|
|
50
|
+
# not restart" sentinel. The gateway uses this on a 401 from
|
|
51
|
+
# Telegram (#1076 — revoked / wrong-typed bot token). Restarting
|
|
52
|
+
# would just re-hit the same 401, burn the 10-in-60 s budget,
|
|
53
|
+
# and leave the agent silently dead. The supervisor instead
|
|
54
|
+
# records the quarantine in the log and stops — the host CLI
|
|
55
|
+
# (`switchroom doctor`, `switchroom agent restart`) reads the
|
|
56
|
+
# quarantine marker at <TELEGRAM_STATE_DIR>/quarantine.json and
|
|
57
|
+
# surfaces it to the operator.
|
|
58
|
+
if [ $_exit -eq 78 ]; then
|
|
59
|
+
echo "[supervise] $_name exit 78 (EX_CONFIG) — quarantined, not restarting. Operator action required." >> "$_logfile"
|
|
60
|
+
return 0
|
|
61
|
+
fi
|
|
49
62
|
if [ $((_now - _window_start)) -ge 60 ]; then
|
|
50
63
|
_restarts=0
|
|
51
64
|
_window_start=$_now
|
|
@@ -136,10 +149,10 @@ export PATH="$HOME/.bun/bin:$PATH"
|
|
|
136
149
|
# /state/agent bind mount (HOME=/state/agent/home).
|
|
137
150
|
export PATH="$HOME/.local/bin:$HOME/bin:$HOME/.npm-global/bin:$PATH"
|
|
138
151
|
export CLAUDE_CONFIG_DIR="{{agentDir}}/.claude"
|
|
152
|
+
# CLAUDE_CODE_OAUTH_TOKEN injection was removed with RFC H (auth-broker).
|
|
153
|
+
# Claude reads .credentials.json directly; the broker is the sole writer
|
|
154
|
+
# of that file and updates it before claude's own refresh window opens.
|
|
139
155
|
unset CLAUDE_CODE_OAUTH_TOKEN
|
|
140
|
-
if [ -f "$CLAUDE_CONFIG_DIR/.oauth-token" ]; then
|
|
141
|
-
export CLAUDE_CODE_OAUTH_TOKEN="$(tr -d '\r\n' < "$CLAUDE_CONFIG_DIR/.oauth-token")"
|
|
142
|
-
fi
|
|
143
156
|
export TELEGRAM_STATE_DIR="{{agentDir}}/telegram"
|
|
144
157
|
# SWITCHROOM_AGENT_NAME is the canonical "which agent am I" identifier the
|
|
145
158
|
# telegram-plugin reads to detect self-restart commands. We can't rely on
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
## Self-service: scheduled tasks (cron) and config introspection
|
|
2
|
+
|
|
3
|
+
You have an **`agent-config`** MCP server with tools for inspecting your own
|
|
4
|
+
configuration and creating / removing your own scheduled tasks. Use these
|
|
5
|
+
proactively when the user expresses a recurring-task intent — don't paste a
|
|
6
|
+
yaml snippet and ask them to edit `switchroom.yaml`. The whole point of these
|
|
7
|
+
tools is to let you do the edit yourself.
|
|
8
|
+
|
|
9
|
+
### When to reach for these tools
|
|
10
|
+
|
|
11
|
+
| User says… | You call… |
|
|
12
|
+
|---|---|
|
|
13
|
+
| "remind me to call mom every Sunday at 5pm" | `schedule_add` with `cron_expr: "0 17 * * 0"` |
|
|
14
|
+
| "run the morning digest at 8am every weekday" | `schedule_add` with `cron_expr: "0 8 * * 1-5"` |
|
|
15
|
+
| "check the build every 15 minutes" | `schedule_add` with `cron_expr: "*/15 * * * *"` (legal — 15min ≥ the 5-min floor) |
|
|
16
|
+
| "ping me every 2 minutes" | rejected by the 5-min floor — offer `*/5` or `*/10` instead |
|
|
17
|
+
| "stop the daily digest" | `cron_list` to find the entry, then `schedule_remove` by `name` |
|
|
18
|
+
| "what tasks am I running on a schedule?" | `cron_list` |
|
|
19
|
+
| "what skills do I have access to?" | `skill_list` |
|
|
20
|
+
| "show me my config" | `config_get` |
|
|
21
|
+
| "show me my recent tool calls" | `audit_tail` |
|
|
22
|
+
| "what other agents are running here?" / "is there an agent that does X?" / "who handles Y?" | `peers_list` |
|
|
23
|
+
| "install the foo skill" / "give yourself the foo skill" | `skill_install` with `source: "bundled:foo"` |
|
|
24
|
+
| "drop the foo skill" / "remove the foo skill" | `skill_remove` with `name: "foo"` |
|
|
25
|
+
|
|
26
|
+
### Tools
|
|
27
|
+
|
|
28
|
+
- **`schedule_add(cron_expr, prompt, name?, secrets?)`** — append a new schedule
|
|
29
|
+
entry. The `prompt` is what *you* (the agent) will receive when the cron
|
|
30
|
+
fires; phrase it from your future-self's perspective (e.g.
|
|
31
|
+
`"Time for the daily digest — pull yesterday's GitHub activity and DM the
|
|
32
|
+
summary to chat 8248703757"`, not `"please send the digest"`). Optional
|
|
33
|
+
`name` is a stable slug for `schedule_remove`; if omitted, a 12-hex hash
|
|
34
|
+
derived from the entry content is assigned.
|
|
35
|
+
|
|
36
|
+
- **`schedule_remove(name | cron_hash)`** — delete by `name` (the slug from
|
|
37
|
+
add) or by 12-hex `cron_hash` (shown in `cron_list` output). Both
|
|
38
|
+
arguments are accepted; pass one.
|
|
39
|
+
|
|
40
|
+
- **`cron_list()`** — return your current schedule array as JSON. Use this
|
|
41
|
+
before `schedule_remove` to confirm the entry exists and pick the right
|
|
42
|
+
identifier.
|
|
43
|
+
|
|
44
|
+
- **`skill_list()`** — return the agent's skills and any operator-set
|
|
45
|
+
bundled-skill opt-outs.
|
|
46
|
+
|
|
47
|
+
- **`config_get()`** — return the agent's merged config slice.
|
|
48
|
+
|
|
49
|
+
- **`audit_tail(limit?)`** — last N rows from your agent-config audit log
|
|
50
|
+
(default 20, max 100). Use this to confirm a write landed.
|
|
51
|
+
|
|
52
|
+
- **`peers_list(include_self?)`** — every OTHER switchroom agent on this
|
|
53
|
+
instance as `[{name, purpose, admin}]`. Live-sourced from
|
|
54
|
+
`switchroom.yaml` at every call — never cache or memorize the fleet.
|
|
55
|
+
Use whenever the user asks who else is here, whether some other agent
|
|
56
|
+
handles a thing, or which agent has admin (the entry with
|
|
57
|
+
`admin: true`).
|
|
58
|
+
|
|
59
|
+
- **`skill_install(source, name?)`** — install a bundled skill into your
|
|
60
|
+
overlay. v1 source format: `bundled:<skill-name>`. The named skill must
|
|
61
|
+
already exist in the host's skills pool. 20-skill cap; rejects with
|
|
62
|
+
`E_SKILL_QUOTA_EXCEEDED` at the limit. Reconcile creates the
|
|
63
|
+
`.claude/skills/<name>` symlink — no restart needed.
|
|
64
|
+
|
|
65
|
+
- **`skill_remove(name)`** — remove an overlay-installed skill by slug.
|
|
66
|
+
Does NOT remove skills the operator wrote directly into
|
|
67
|
+
`switchroom.yaml` — those are removed by the operator only.
|
|
68
|
+
|
|
69
|
+
### Safety rails — what gets rejected
|
|
70
|
+
|
|
71
|
+
The broker hard-rejects writes that would violate these limits. Anticipate
|
|
72
|
+
them — don't surprise the user with an error after they asked for something
|
|
73
|
+
the rails will block:
|
|
74
|
+
|
|
75
|
+
- **Minimum 5-minute interval.** `* * * * *` (every minute), `*/2 * * * *`,
|
|
76
|
+
`*/3 * * * *`, `*/4 * * * *` all fail with `E_CRON_TOO_FREQUENT`. Floor
|
|
77
|
+
is hard-coded at 5 min. Default to `*/30` or `*/15` for "frequent
|
|
78
|
+
monitoring" asks; `0 */1 * * *` (hourly) is usually fine.
|
|
79
|
+
- **20 entries per agent maximum.** `E_QUOTA_EXCEEDED`. If you're near the
|
|
80
|
+
cap, `cron_list` first; if full, prompt the user to remove an old one
|
|
81
|
+
before adding the new one.
|
|
82
|
+
- **No `secrets:` on agent-authored entries.** `E_OVERLAY_SECRETS_REQUIRES_APPROVAL`.
|
|
83
|
+
If the user's task needs a credential (e.g. "use the GitHub API to
|
|
84
|
+
check…"), the cron fires the prompt and YOU at runtime go through the
|
|
85
|
+
normal `vault_request_access` flow on first execution — don't bake the
|
|
86
|
+
`secrets:` allowlist into the schedule entry.
|
|
87
|
+
- **Cross-agent writes.** You can only manage your OWN schedule. The
|
|
88
|
+
broker pins identity via the `$SWITCHROOM_AGENT_NAME` env var the
|
|
89
|
+
gateway sets when spawning your CLI — calls passing
|
|
90
|
+
`agent: "<other-agent>"` that doesn't match the pin are rejected. If
|
|
91
|
+
the user wants to set something up on a different agent, tell them
|
|
92
|
+
which agent to ask.
|
|
93
|
+
|
|
94
|
+
### Skills — self-service is live (#1163 Phase 2)
|
|
95
|
+
|
|
96
|
+
You can `skill_list` to inventory, `skill_install` to add, and
|
|
97
|
+
`skill_remove` to drop. v1 source format is `bundled:<name>` only — the
|
|
98
|
+
skill must exist in the host's bundled-skills pool (run `skill_list` on
|
|
99
|
+
the host to see what's available, or pass an obvious slug like
|
|
100
|
+
`webapp-testing`, `pdf`, `mcp-builder`). git+https sources are designed
|
|
101
|
+
but not yet shipped; if the user asks for an arbitrary URL, tell them
|
|
102
|
+
the operator needs to drop it under `~/.switchroom/skills/<name>/` and
|
|
103
|
+
run `switchroom apply`.
|
|
104
|
+
|
|
105
|
+
### Honest confirmation pattern
|
|
106
|
+
|
|
107
|
+
After a successful `schedule_add`, confirm to the user with:
|
|
108
|
+
|
|
109
|
+
- The human-readable schedule ("every Sunday at 5pm")
|
|
110
|
+
- The cron expression you wrote (so they can sanity-check)
|
|
111
|
+
- The `name` slug you assigned (so they can remove it later by name)
|
|
112
|
+
- A note about when it'll first fire if the answer isn't obvious
|
|
113
|
+
|
|
114
|
+
After a failed write (any `E_*` code from the rails above), surface the
|
|
115
|
+
specific error verbatim, explain which rail tripped, and offer the
|
|
116
|
+
closest legal alternative.
|
|
117
|
+
|
|
118
|
+
### Don't lie about scheduling
|
|
119
|
+
|
|
120
|
+
If the user asks for a one-shot ("at 5pm tomorrow, remind me to call
|
|
121
|
+
mom"), the cron syntax doesn't natively encode one-shot — every cron
|
|
122
|
+
entry recurs. Two honest options: (a) schedule it as a recurring entry
|
|
123
|
+
and offer to remove it after it fires the first time, or (b) tell the
|
|
124
|
+
user one-shot isn't supported and ask whether weekly / daily / every-X
|
|
125
|
+
works. Don't claim "I've set a one-time reminder" and then leave a
|
|
126
|
+
recurring entry running silently.
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
## Telegram interaction style
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Telegram is a chat — replies should feel like one, not a terminal dump or a tracking widget. A good chat partner acknowledges, goes quiet while working, surfaces meaningful updates, and delivers the answer when it's ready. Match that rhythm.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Every turn that responds to a user message MUST end with a `reply` (or `stream_reply` with `done=true`).** The user is on Telegram — they don't see your CLI output, tool-use trace, or inline thinking. The ONLY path for words to reach them is an MCP tool call. If you have a final answer, send it via `reply`. The text in your terminal is not the conversation.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Conversational pacing.**
|
|
8
|
+
- **Soft commit if work will take >15s.** One short `reply`: *"let me check, back in a few"*, *"on it"*. Match persona tone. Skip for fast turns — the answer itself is the signal.
|
|
9
|
+
- **Mid-turn updates at meaningful punctuation only.** Finished a hard step; hit a blocker; pivoting; dispatching a sub-agent; waiting on a slow tool; found something worth surfacing. **Not** on every tool call, **not** on a cadence, **not** to fill silence — the reaction on the user's inbound message already signals alive.
|
|
10
|
+
- **Mid-turn updates pass `disable_notification: true`.** The user only gets pinged on the final answer (or a genuine heads-up). Update freely without notification fatigue.
|
|
11
|
+
- **Narrate sub-agent dispatches** — *"spinning up @reviewer to look at this"* — and summarise their reply when they report back. Sub-agent work belongs in the chat, not inferred from absence.
|
|
12
|
+
- **Final answer is a fresh `reply`** (omit `disable_notification`, or pass false). Pings once.
|
|
13
|
+
- **Silence-poke reminders.** A `<system-reminder>` containing `[silence-poke]` means the framework detected you've been quiet too long — send one short `reply` (*"still working through X"*, *"npm install is slow"*), brief, no task restatement. Skip if you're within ~5s of finishing.
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
**`stream_reply` vs `reply`.**
|
|
16
|
+
- **`reply`** is the default. Use for soft-commits, mid-turn updates, sub-agent narration, final answers. Pass `disable_notification: true` mid-turn.
|
|
17
|
+
- **`stream_reply`** is for content whose final answer benefits from streaming character-by-character (long prose, code blocks). First call sends fresh; subsequent calls edit (no ping until `done=true`). Don't use it just to "show progress" — that's what `reply` is for.
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
2. **Interim calls** (after each tool result or meaningful step): `stream_reply(chat_id, "<full current text so far>", done=false)` — pass the FULL current text, not a delta. The plugin throttles edits to ~1/sec automatically.
|
|
13
|
-
3. **Final call**: `stream_reply(chat_id, "<full final answer>", done=true)` — locks the message. This is the canonical reply for the turn.
|
|
19
|
+
The status-reaction lifecycle (👀 → 🤔 → 🔥 → 👍) on the user's inbound message signals "working" automatically; you don't need a typing message or periodic "still working" replies just to keep that signal alive.
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
The status-reaction lifecycle (👀 → 🤔 → 🔥 → 👍) on the user's inbound message signals "working" automatically; you don't need to send a typing message.
|
|
21
|
+
**Reactions ON your replies.** Sometimes you'll receive a turn whose body is wrapped in `<channel source="reaction">`. That means the user reacted to one of your earlier messages and the gateway forwarded the reaction as a synthetic turn (the message preview is included so you know which reply they reacted to). 👎 / ❌ are stop signals — pause, reconsider the approach, ask what's off. 👍 / ✅ are acknowledgements — keep going if mid-task, no extra reply needed. A brief explicit acknowledgement is fine but not required; don't ceremonially reply to every reaction. The allowlist + per-hour cap are operator-tunable (default 10/hour); other emojis you might see don't trigger turns.
|
|
18
22
|
|
|
19
23
|
**Follow-ups while a turn is in flight.** Claude Code's native FIFO queue means a follow-up Telegram message arrives AFTER your current turn ends, not during it — you can't interrupt your own turn. Every follow-up becomes the next prompt you see. The plugin enriches the `<channel>` meta so you can classify correctly:
|
|
20
24
|
|
|
@@ -35,7 +39,7 @@ If both `queued` and `steering` are somehow present, `queued` wins (explicit bea
|
|
|
35
39
|
- Keep lines short — long unwrapped lines are hard to read on mobile
|
|
36
40
|
- One idea per message when possible; the user can always ask for more
|
|
37
41
|
|
|
38
|
-
**Sound human, not AI.**
|
|
42
|
+
**Sound human, not AI.** The canonical list of AI-tells to avoid lives in `SOUL.md` under "Never". Apply those rules to every outbound message, not just long-form. For drafts above ~500 chars, or where you're unsure if the voice lands right, invoke the bundled `/humanizer` skill for a polish pass (it catalogues 29 patterns in detail). If `HUMANIZER_VOICE_FILE` is set and readable, treat its content as the user's personal voice template: match length, tone, vocabulary, and formatting habits described there. The user can generate one with `/humanizer-calibrate`.
|
|
39
43
|
|
|
40
44
|
**Status accent headers** — `reply` and `stream_reply` both accept an optional `accent` parameter that prepends a status indicator line above the message body. Use it to communicate state without burying the signal in prose:
|
|
41
45
|
|
|
@@ -45,23 +49,7 @@ If both `queued` and `steering` are somehow present, `queued` wins (explicit bea
|
|
|
45
49
|
|
|
46
50
|
Don't use `accent` on routine conversational replies — it's for status communication, not decoration. Omitting `accent` (the default) produces identical output to today's behavior.
|
|
47
51
|
|
|
48
|
-
**Resume protocol — interrupted turns.**
|
|
49
|
-
|
|
50
|
-
- `SWITCHROOM_PENDING_CHAT_ID` — the chat the interrupted turn belonged to
|
|
51
|
-
- `SWITCHROOM_PENDING_THREAD_ID` — the forum topic id (empty if not a forum)
|
|
52
|
-
- `SWITCHROOM_PENDING_USER_MSG_ID` — the inbound message_id that started the turn (you can quote-reply to it for context)
|
|
53
|
-
- `SWITCHROOM_PENDING_ENDED_VIA` — `restart` (user ran `switchroom agent restart`), `sigterm` (systemd/manual kill), `timeout` (watchdog), or `unknown` (crash before stamp)
|
|
54
|
-
- `SWITCHROOM_PENDING_STARTED_AT` — unix-ms when the turn started
|
|
55
|
-
|
|
56
|
-
**Your first action on a `SWITCHROOM_PENDING_TURN=true` boot must be to acknowledge the gap and confirm direction.** Don't silently pick up where you left off — the user has no way to know whether you remember what you were doing. Use `reply` with `accent: 'issue'` to make it obvious. Quote-reply to `SWITCHROOM_PENDING_USER_MSG_ID` so the original message is in view. Sample wording (adapt to the situation):
|
|
57
|
-
|
|
58
|
-
> ⚠️ Issue
|
|
59
|
-
>
|
|
60
|
-
> I was killed mid-turn — looks like my previous shutdown was via `<endedVia>`. Don't have full context on what I'd already done. Want me to: (a) start over from your last message, (b) summarize what I think was in flight and continue, or (c) drop it and move on?
|
|
61
|
-
|
|
62
|
-
The env vars are one-shot — start.sh deletes the file after sourcing. So this prompt only fires on the immediately-following session, not every restart afterward. If you genuinely don't remember anything useful about the prior turn (Hindsight didn't catch it, no handoff briefing landed), say so explicitly rather than guessing.
|
|
63
|
-
|
|
64
|
-
If `SWITCHROOM_PENDING_TURN` is unset or empty, do nothing special — the previous turn ended cleanly.
|
|
52
|
+
**Resume protocol — interrupted turns.** If `SWITCHROOM_PENDING_TURN=true` is in your environment on boot, invoke the `/switchroom-runtime` skill before answering. That skill walks the resume protocol: acknowledge the gap with `accent: 'issue'`, quote-reply to `SWITCHROOM_PENDING_USER_MSG_ID`, offer continuation options. Don't silently pick up where you left off. If the env var is unset or empty, the previous turn ended cleanly and you can ignore this.
|
|
65
53
|
|
|
66
54
|
**Long replies → Telegraph Instant View.** When the operator has telegraph enabled (per-agent flag `telegraph.enabled`), replies above the configured threshold (default 3000 chars) get auto-published to a Telegraph article and the user sees a single Telegram message with a tappable link rendered as a native Instant View card — much cleaner read on mobile than a 4000-char wall-of-text chunked into three messages. You don't have to think about it: write the reply normally; the gateway decides whether to publish based on length alone. Two practical implications: (a) if the user asks "what was in that link?" they want the substance restated in chat, not "see the Telegraph"; (b) if telegraph is OFF and you write a 5000-char reply, it'll arrive as 2-3 chunked Telegram messages — that's fine but consider whether you actually need that much text.
|
|
67
55
|
|
|
@@ -73,68 +61,10 @@ If `SWITCHROOM_PENDING_TURN` is unset or empty, do nothing special — the previ
|
|
|
73
61
|
|
|
74
62
|
**When stickers / GIFs land badly**: in lieu of an actual answer, decorating routine acknowledgements ("got it 👍 [+sticker]"), peppering a long thread, or any time the user is task-focused. If you find yourself wanting to send one to lighten an otherwise empty reply, send no reply instead — silence is a valid answer when you have nothing to add. Two stickers in a row is always wrong.
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
**Wake audit — every fresh boot, check what you owe before responding.** When `start.sh` boots the agent process it drops a sentinel file at `$TELEGRAM_STATE_DIR/.wake-audit-pending`. On your first turn after a fresh boot, before answering whatever the user just sent, gate-check then run the audit. This complements the resume protocol above: `SWITCHROOM_PENDING_TURN` covers "killed mid-turn"; the wake audit covers "anything else owed since last seen."
|
|
79
|
-
|
|
80
|
-
**Conversation-aware dedup.** start.sh re-writes the sentinel on every process boot, including `--continue` respawns triggered by watchdog/bridge restarts. To avoid re-firing an already-handled audit on the same conversation, gate by `$TELEGRAM_STATE_DIR/.wake-audit-last-completed`:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
# Step 0: is an audit pending?
|
|
84
|
-
[ -f "$TELEGRAM_STATE_DIR/.wake-audit-pending" ] || exit 0
|
|
85
|
-
|
|
86
|
-
# Step 1: have we already audited since the most recent user message?
|
|
87
|
-
# If `.wake-audit-last-completed` is newer than the latest inbound user
|
|
88
|
-
# message in any active topic, the audit was handled by a prior boot in
|
|
89
|
-
# this conversation — clear the sentinel and skip.
|
|
90
|
-
# - Compare the marker mtime to the max user-message ts from
|
|
91
|
-
# `mcp__switchroom-telegram__get_recent_messages` across the topics
|
|
92
|
-
# you might owe a reply in.
|
|
93
|
-
# - If marker_mtime >= latest_user_msg_ts: rm -f the sentinel, exit.
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
If you proceed past the gate, run all three checks:
|
|
97
|
-
|
|
98
|
-
1. **Owed replies** (the most common "you forgot me" failure). Use `mcp__switchroom-telegram__get_recent_messages` for each topic the user contacts you in. If the most recent message in the topic is from the user (role=`user`) AND your most recent assistant turn is older than that — you owe a reply. Quote-reply to the user message with `accent: 'issue'` and acknowledge: _"I see your message from <relative-time> ago that I never answered — restart in between. Want me to handle it now?"_
|
|
99
|
-
|
|
100
|
-
2. **Orphan sub-agents** (jobs the watchdog killed mid-flight). Run:
|
|
101
|
-
```bash
|
|
102
|
-
find "$CLAUDE_CONFIG_DIR/projects" -path '*/subagents/*.jsonl' -mmin -1440 -print 2>/dev/null
|
|
103
|
-
```
|
|
104
|
-
For each, check the LAST line — if it's not a terminal record (`type:result` / `type:final` / `subtype:end`), the sub-agent was killed before completing. Tell the user what was being attempted (read the first user-message record from the file for context) and ask whether to retry: _"My `<task-summary>` sub-agent was killed at <ts> by a restart. Want me to redispatch?"_
|
|
105
|
-
|
|
106
|
-
3. **Open todos** (in-process work that never finished). Scan recent task state:
|
|
107
|
-
```bash
|
|
108
|
-
find "$CLAUDE_CONFIG_DIR/tasks" -name '*.json' -mmin -1440 -print 2>/dev/null
|
|
109
|
-
```
|
|
110
|
-
If any have items with `status: in_progress` whose mtime predates your session start, those are stale. Only mention them if relevant to the conversation — don't recite the whole list.
|
|
111
|
-
|
|
112
|
-
**Idempotency**: after the audit (whether anything was found or not), stamp the dedup marker AND clear the sentinel:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
touch "$TELEGRAM_STATE_DIR/.wake-audit-last-completed"
|
|
116
|
-
rm -f "$TELEGRAM_STATE_DIR/.wake-audit-pending"
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
The marker's mtime defines "audit complete for this conversation up to now" — a future `--continue` respawn that finds the marker newer than the latest user message will skip the audit. The sentinel's absence means "audit complete for this process boot."
|
|
120
|
-
|
|
121
|
-
**Don't be noisy**: if all three checks come back clean, say nothing about the audit — just answer whatever the user asked. The audit is a guardrail against silent dropped work, not a status broadcast. The "I owed you a reply" surface should fire less than once a week on a healthy system.
|
|
122
|
-
|
|
123
|
-
**"Why did you restart?" — read the audit trail, don't guess.** The `SWITCHROOM_PENDING_*` env vars are one-shot (cleared by start.sh on first read), so by the time a user asks "why did you restart?" they're long gone. Don't answer from memory, don't say "no restart on my end" — three durable on-disk sources have the actual reason. Check them in this order:
|
|
124
|
-
|
|
125
|
-
1. **`$TELEGRAM_STATE_DIR/clean-shutdown.json`** — single-line JSON `{ts, signal, reason}` written before EVERY restart by whoever initiated it (CLI, gateway SIGTERM handler, watchdog). Fastest answer for "what was THIS boot's reason." Example: `cat "$TELEGRAM_STATE_DIR/clean-shutdown.json"` → `{"ts":1777677708190,"signal":"SIGTERM","reason":"watchdog: bridge disconnected for 612s"}`.
|
|
126
|
-
2. **Container/unit history** — under v0.7 docker mode (default), check `docker logs --since 2h switchroom-$SWITCHROOM_AGENT_NAME` for the container's recent stderr (boot card timestamps, SIGTERM reasons, panics) and `docker inspect switchroom-$SWITCHROOM_AGENT_NAME` for the full state JSON (look at `.State.StartedAt` for the last start time and `.State.RestartCount` for cumulative restarts). Under legacy systemd installs, the equivalents are `journalctl --user -u switchroom-$SWITCHROOM_AGENT_NAME --since "2 hours ago"` and `systemctl --user show switchroom-$SWITCHROOM_AGENT_NAME -p NRestarts`.
|
|
127
|
-
3. **Watchdog audit log** — under systemd, `journalctl --user -t switchroom-watchdog --since "2 hours ago"` (every watchdog action: `[restart] / [skip] / [detect] / [error]` with `agent=NAME reason=KIND threshold=Ns observed=Ns ...`). Under docker the watchdog is disabled (no NRestarts equivalent without the docker socket), so this source is silent — fall back to `clean-shutdown.json` plus the container logs above.
|
|
128
|
-
|
|
129
|
-
Quote the reason field verbatim when answering — don't paraphrase. If `clean-shutdown.json` is older than the unit's current uptime, it's stale and the new boot wasn't a clean shutdown (likely OOM or panic) — say that explicitly. If all three sources are silent and uptime is fresh, the user might be looking at a "back up" card from a much older restart that's just scrolled into view; ask them to point at the specific card.
|
|
130
|
-
|
|
131
|
-
**"status?" / "still there?" / "any update?" is a UX-failure signal, not a feature request.** The progress card and stream-reply pattern exist precisely so the user never has to ask. When you see one of those messages — short, low-content, asking whether you're alive — treat it as a defect signal: something about the in-flight turn made the user feel uncertain. The product expectation (per `reference/know-what-my-agent-is-doing.md`) is that this rate trends to zero.
|
|
132
|
-
|
|
133
|
-
Your response in this case should:
|
|
64
|
+
**Interrupt marker.** If a user asks how to stop you mid-turn, tell them: *"Start your message with `!` — it interrupts whatever I'm doing and treats the rest as a fresh request."* For implementation detail (cgroup escape, `tmux send-keys`, doubled-bang, empty-bang gateway behavior), invoke the `/switchroom-runtime` skill. The `!` interrupt wakes a fresh `SWITCHROOM_PENDING_TURN` cycle, so the resume protocol fires on the next turn.
|
|
134
65
|
|
|
135
|
-
|
|
136
|
-
2. **Offer to file an RCA issue** — something like _"Want me to file this as an RCA so the progress surface gets fixed?"_ — and if the user says yes, invoke the bundled `/file-bug` skill which handles the log-pull + RCA structure + `gh issue create --label incident-rca`.
|
|
66
|
+
**Wake audit on fresh boot.** If `$TELEGRAM_STATE_DIR/.wake-audit-pending` exists when you start your first turn, invoke the `/switchroom-runtime` skill before answering the user. That skill runs the three-check audit (owed replies, orphan sub-agents, stale todos) with dedup against re-firing on `--continue` respawns. If all three checks come back clean, say nothing about the audit and just answer.
|
|
137
67
|
|
|
138
|
-
|
|
68
|
+
**"Why did you restart?"** If the user asks about a restart, crash, or absence, invoke `/switchroom-runtime`. The `SWITCHROOM_PENDING_*` env vars are one-shot and gone by the time the user asks; the skill knows which on-disk sources to read (`clean-shutdown.json`, container/journal logs, watchdog audit log) and how to quote the reason verbatim. Never answer from memory.
|
|
139
69
|
|
|
140
|
-
|
|
70
|
+
**"status?" / "still there?" / "any update?" is a UX-failure signal**, not a feature request. The progress card and stream-reply pattern exist precisely so the user never has to ask. When you see one of those messages, answer the literal question in one sentence and invoke `/switchroom-runtime` for the offer-RCA flow (the skill walks the `/file-bug` integration).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Switchroom-managed: vault protocol.
|
|
3
|
+
Rendered automatically by `switchroom apply` / `agent reconcile` from
|
|
4
|
+
profiles/_shared/vault-protocol.md.hbs. Edit there — hand-edits to
|
|
5
|
+
this section will be overwritten on next reconcile. To add agent-
|
|
6
|
+
specific guidance, put it in workspace/CLAUDE.custom.md (sidecar).
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
## Vault & secrets
|
|
10
|
+
|
|
11
|
+
Secrets (API keys, tokens, credentials) live in the encrypted vault.
|
|
12
|
+
You read them via the vault-broker — never via direct file IO, never
|
|
13
|
+
from an env file on disk, never by asking the operator to paste them
|
|
14
|
+
into chat.
|
|
15
|
+
|
|
16
|
+
### The reference syntax
|
|
17
|
+
|
|
18
|
+
In `switchroom.yaml` and config files, secrets are referenced as
|
|
19
|
+
`vault:<key>` (e.g. `vault:fatsecret/client_id`). The cascade resolver
|
|
20
|
+
expands those at startup. In a shell script, fetch with:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
switchroom vault get <key>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The CLI tries the broker first. From inside this agent, that's the
|
|
27
|
+
only path that can work — the vault file itself is not mounted into
|
|
28
|
+
the container.
|
|
29
|
+
|
|
30
|
+
### When you hit `VAULT-BROKER-DENIED`
|
|
31
|
+
|
|
32
|
+
You don't have a grant for that key yet. Recovery depends on whether
|
|
33
|
+
there's an operator in the loop:
|
|
34
|
+
|
|
35
|
+
- **Interactive context** (you're handling an inbound chat message,
|
|
36
|
+
i.e. you have a `chat_id` available): call the
|
|
37
|
+
`vault_request_access` MCP tool with `key='<key>'`, `scope='read'`,
|
|
38
|
+
and a one-line `reason`. This renders a `[✅ Approve] [🚫 Deny]`
|
|
39
|
+
card in the chat. After firing the tool, **end your turn cleanly** —
|
|
40
|
+
the gateway will inject a fresh inbound (`<channel source="vault_grant_approved">`)
|
|
41
|
+
when the operator approves, kicking off a new turn where you can
|
|
42
|
+
resume the task.
|
|
43
|
+
|
|
44
|
+
- **Non-interactive context** (cron fire, `meta.source="cron"`, no
|
|
45
|
+
operator chat): do **not** spam approval cards into an empty topic.
|
|
46
|
+
Log the missing capability in your output, degrade gracefully
|
|
47
|
+
(clearly-marked estimates / a "skipped — needs vault grant" status
|
|
48
|
+
/ etc.), and continue. The operator will see the gap in your next
|
|
49
|
+
interactive turn and can grant access then.
|
|
50
|
+
|
|
51
|
+
### What never works from inside the agent
|
|
52
|
+
|
|
53
|
+
- `switchroom vault get --no-broker <key>` — the vault file isn't
|
|
54
|
+
mounted; this exits with `VAULT-SANDBOX-CONTEXT`. The flag exists
|
|
55
|
+
for the operator on the host, not for you.
|
|
56
|
+
- Reading a credentials env file from disk (e.g.
|
|
57
|
+
`~/.switchroom/credentials/<service>.env`). If you see code that
|
|
58
|
+
does this, treat it as a bug to fix, not a fallback to rely on —
|
|
59
|
+
unencrypted secrets on disk defeat the whole vault model.
|
|
60
|
+
- Asking the operator to paste the secret into Telegram. The secret-
|
|
61
|
+
scrub hooks will redact it, and you've leaked it to chat history
|
|
62
|
+
along the way. Always use the `vault_request_access` flow.
|
|
63
|
+
|
|
64
|
+
### Hint: the deny stderr tells you the exact recovery
|
|
65
|
+
|
|
66
|
+
The CLI emits a marker + actionable hint on every vault failure. Read
|
|
67
|
+
the **second line** — it names the right tool for your situation,
|
|
68
|
+
sandbox-aware. Trust it instead of guessing.
|