switchroom 0.18.29 → 0.18.30
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/bin/handoff-briefing.sh +8 -2
- package/dist/agent-scheduler/index.js +111 -7
- package/dist/auth-broker/index.js +154 -16
- package/dist/cli/autoaccept-poll.js +8 -3
- package/dist/cli/drive-write-pretool.mjs +8 -3
- package/dist/cli/ms-365-write-pretool.mjs +158 -11
- package/dist/cli/notion-write-pretool.mjs +103 -4
- package/dist/cli/switchroom.js +2074 -1585
- package/dist/host-control/main.js +110 -13
- package/dist/vault/approvals/kernel-server.js +116 -13
- package/dist/vault/broker/server.js +314 -145
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +73 -20
- package/telegram-plugin/dist/bridge/bridge.js +71 -47
- package/telegram-plugin/dist/gateway/gateway.js +560 -96
- package/telegram-plugin/dist/server.js +89 -64
- package/telegram-plugin/gateway/gateway.ts +212 -17
- package/telegram-plugin/gateway/model-command.ts +104 -0
- package/telegram-plugin/gateway/session-model-file.ts +40 -0
- package/telegram-plugin/gateway/unhandled-message.ts +177 -0
- package/telegram-plugin/llm-error-present.ts +24 -0
- package/telegram-plugin/model-unavailable.ts +55 -0
- package/telegram-plugin/operator-events.ts +113 -0
- package/telegram-plugin/pending-user-notice.ts +88 -0
- package/telegram-plugin/shared/local-time.ts +43 -0
- package/telegram-plugin/tests/catch-all-forwarded-history.test.ts +103 -0
- package/telegram-plugin/tests/catch-all-unhandled-message.test.ts +264 -0
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +26 -2
- package/telegram-plugin/tests/litellm-proxy-auth-misconfig.test.ts +278 -0
- package/telegram-plugin/tests/local-time.test.ts +68 -1
- package/telegram-plugin/tests/model-command.test.ts +133 -0
- package/telegram-plugin/tests/session-model-file.test.ts +23 -0
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +399 -2
- package/vendor/hindsight-memory/scripts/lib/client.py +47 -0
- package/vendor/hindsight-memory/scripts/lib/content.py +53 -1
- package/vendor/hindsight-memory/scripts/lib/turnlog.py +450 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +467 -0
- package/vendor/hindsight-memory/tests/test_content.py +35 -0
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.18.
|
|
4
|
+
"version": "0.18.30",
|
|
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/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/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/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/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 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-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/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/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/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-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/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/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-no-pii-secrets.mjs && node scripts/check-vault-test-hermeticity.mjs && node scripts/check-no-broadcast-delivery.mjs && node scripts/check-stale-tool-descriptions.mjs && node scripts/check-web-subscription-honest.mjs && node scripts/check-no-unpinned-npx-playwright.mjs",
|
|
32
32
|
"lint:tsc": "tsc --noEmit",
|
|
@@ -1182,10 +1182,17 @@ _EFFECTIVE_MODEL={{{modelQ}}}
|
|
|
1182
1182
|
printf '%s\n' "$_EFFECTIVE_MODEL" > "{{agentDir}}/.configured-default-model" 2>/dev/null || true
|
|
1183
1183
|
|
|
1184
1184
|
# Rev-4 hygiene (#3184 review LOW-2): remove the files retired with the
|
|
1185
|
-
# keep/revert intent subsystem
|
|
1186
|
-
#
|
|
1187
|
-
#
|
|
1188
|
-
rm -f "{{agentDir}}/.relaunch-model-intent" "{{agentDir}}/.session-model-
|
|
1185
|
+
# keep/revert intent subsystem. Nothing reads them anymore; without this rm they
|
|
1186
|
+
# would linger in the bind-mounted state dir forever after rollout. Idempotent —
|
|
1187
|
+
# a cheap no-op once clean.
|
|
1188
|
+
rm -f "{{agentDir}}/.relaunch-model-intent" "{{agentDir}}/.session-model-kept-notified"
|
|
1189
|
+
# `.session-model-boot-attempts` is REVIVED as LIVE state (#3284 bounded-retry
|
|
1190
|
+
# counter, see the resolution block below): the gateway clears it on a healthy
|
|
1191
|
+
# boot, and start.sh gives up + reverts once it exceeds the attempt bound. It is
|
|
1192
|
+
# meaningless without a carrier to belong to, so sweep it as stale ONLY when no
|
|
1193
|
+
# `.session-model` carrier is present (rollout leftover / completed apply / a
|
|
1194
|
+
# prior giveup that didn't get to delete it).
|
|
1195
|
+
[ -f "{{agentDir}}/.session-model" ] || rm -f "{{agentDir}}/.session-model-boot-attempts"
|
|
1189
1196
|
|
|
1190
1197
|
# Migration shim (one release): a leftover one-shot `.session-model-override`
|
|
1191
1198
|
# carrier from a pre-consume-once gateway means an OLD gateway wrote it
|
|
@@ -1207,11 +1214,34 @@ if [ -f "{{agentDir}}/.session-model" ]; then
|
|
|
1207
1214
|
_smf="$(cat "{{agentDir}}/.session-model" 2>/dev/null || true)"
|
|
1208
1215
|
_sm_model="$(printf '%s' "$_smf" | sed -n 's/.*"model"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
|
1209
1216
|
_sm_cfg="$(printf '%s' "$_smf" | sed -n 's/.*"configuredDefaultAtWrite"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
|
1210
|
-
#
|
|
1211
|
-
#
|
|
1212
|
-
#
|
|
1213
|
-
#
|
|
1214
|
-
|
|
1217
|
+
# BOUNDED-RETRY CONSUME (#3284) — the carrier is NO LONGER deleted here before
|
|
1218
|
+
# the apply/exec. It used to be (pure consume-once), but a boot that reads the
|
|
1219
|
+
# carrier may WEDGE before its gateway acquires the boot lock (the
|
|
1220
|
+
# boot.lock_stale_recovered_boot_mismatch race, e.g. a proxy-only `fable`
|
|
1221
|
+
# apply-boot) and never become the surviving healthy session. Deleting before
|
|
1222
|
+
# apply handed the requested model to that loser: the retry boot found no
|
|
1223
|
+
# carrier and silently reverted to the configured default. Instead:
|
|
1224
|
+
#
|
|
1225
|
+
# - the GATEWAY deletes the carrier + the attempt counter once it ACQUIRES
|
|
1226
|
+
# the boot lock (boot.lock_acquired in gateway.ts — the healthy-boot signal
|
|
1227
|
+
# a wedged boot cannot fake), so a GOOD token survives a transient wedge
|
|
1228
|
+
# and RE-APPLIES on the retry boot instead of reverting;
|
|
1229
|
+
# - a persisted attempt counter (`.session-model-boot-attempts`) BOUNDS the
|
|
1230
|
+
# retries: a token that never reaches a healthy gateway (a genuinely BAD
|
|
1231
|
+
# token that crashes claude before lock-acquire) is given up after
|
|
1232
|
+
# _SM_MAX_ATTEMPTS boots — deleted, reverted, and alerted — so a bad token
|
|
1233
|
+
# can NEVER produce an unbounded wedge→retry→wedge crashloop. The original
|
|
1234
|
+
# "a bad token crashes at most one boot then reverts" guarantee becomes "at
|
|
1235
|
+
# most _SM_MAX_ATTEMPTS boots then reverts", still a hard finite bound.
|
|
1236
|
+
#
|
|
1237
|
+
# Every INVALIDATION / GIVEUP branch below deletes BOTH the carrier and the
|
|
1238
|
+
# counter; only the APPLY branch keeps the carrier (for the gateway to clear)
|
|
1239
|
+
# and persists the incremented counter. The delete-before-apply that used to
|
|
1240
|
+
# sit here is GONE by design.
|
|
1241
|
+
_SM_MAX_ATTEMPTS=3
|
|
1242
|
+
_sm_attempts="$(cat "{{agentDir}}/.session-model-boot-attempts" 2>/dev/null | tr -dc '0-9' || true)"
|
|
1243
|
+
[ -z "$_sm_attempts" ] && _sm_attempts=0
|
|
1244
|
+
_sm_attempts=$(( _sm_attempts + 1 ))
|
|
1215
1245
|
# Shape gate — kept BYTE-IDENTICAL with MODEL_ARG_RE in
|
|
1216
1246
|
# telegram-plugin/gateway/model-command.ts. `/` is allowed for
|
|
1217
1247
|
# OpenRouter-style `sr-vendor/model` ids; it is not a shell metachar inside
|
|
@@ -1222,33 +1252,56 @@ if [ -f "{{agentDir}}/.session-model" ]; then
|
|
|
1222
1252
|
# `claude --model` (parity with parseSessionModel's single-string check).
|
|
1223
1253
|
if [ "$(printf '%s' "$_sm_model" | wc -c)" -eq 0 ] || [ "$(printf '%s' "$_sm_model" | wc -l)" -ne 0 ] || ! printf '%s' "$_sm_model" | grep -Eq '^[A-Za-z0-9][]A-Za-z0-9._[/-]{0,99}$'; then
|
|
1224
1254
|
# Invalid carrier: fall back to the configured default AND tell the
|
|
1225
|
-
# operator once — never a silent stderr-only drop.
|
|
1255
|
+
# operator once — never a silent stderr-only drop. Terminal → drop the
|
|
1256
|
+
# carrier + counter (a corrupt token is never worth retrying).
|
|
1257
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1226
1258
|
echo "session-model: ignoring malformed .session-model (failed shape gate) — using configured default '$_EFFECTIVE_MODEL'" >&2
|
|
1227
1259
|
printf 'Your saved session model override could not be read (invalid or corrupt), so the agent booted on its configured default `%s`. Re-issue /model <name> if you want a different model.\n' "$_EFFECTIVE_MODEL" > "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1228
1260
|
elif [ "$_sm_cfg" != "$_EFFECTIVE_MODEL" ]; then
|
|
1229
1261
|
# switchroom.yaml `model:` changed between the switch and this apply-boot →
|
|
1230
1262
|
# the carrier is against a default that no longer exists. Invalidate +
|
|
1231
|
-
# announce once.
|
|
1263
|
+
# announce once. Terminal → drop the carrier + counter.
|
|
1264
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1232
1265
|
echo "session-model: configured default changed ('$_sm_cfg' → '$_EFFECTIVE_MODEL') — dropping session override '$_sm_model'" >&2
|
|
1233
1266
|
printf 'The configured default model changed (`%s` → `%s`), so your session override to `%s` was not applied — the agent booted on the new configured default. Re-issue /model %s if you still want it.\n' "$_sm_cfg" "$_EFFECTIVE_MODEL" "$_sm_model" "$_sm_model" > "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1234
1267
|
elif { [ "${_sm_model#sr-}" != "$_sm_model" ] || [ "$_sm_model" = fable ] || [ "$_sm_model" = claude-fable-5 ]; } && [ -z "$_LITELLM_OK" ]; then
|
|
1235
1268
|
# Proxy-only override + LiteLLM unreachable at the apply-boot: booting the
|
|
1236
1269
|
# override would 4xx (sr-* against Anthropic; `fable`/`claude-fable-5` is a
|
|
1237
1270
|
# retired codename direct-to-Anthropic and ONLY resolves via the LiteLLM
|
|
1238
|
-
# router — see the repoint case below).
|
|
1239
|
-
#
|
|
1240
|
-
#
|
|
1241
|
-
#
|
|
1242
|
-
# the
|
|
1271
|
+
# router — see the repoint case below). The litellm probe already waited out
|
|
1272
|
+
# its full 120s window before we got here, so "still unreachable" is a real
|
|
1273
|
+
# down-state, not the transient boot-lock wedge the bounded-retry counter
|
|
1274
|
+
# covers — drop the carrier + counter (terminal) and tell the operator to
|
|
1275
|
+
# re-issue once the proxy is back, rather than looping against a dead proxy.
|
|
1276
|
+
# (Rev 5: Fable is now reachable via the carrier relaunch — the old
|
|
1277
|
+
# live-inject path never launched it — so it needs the same down-guard as sr-*.)
|
|
1278
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1243
1279
|
echo "session-model: LiteLLM proxy unreachable at boot — NOT applying proxy-only override '$_sm_model'; booting configured default '$_EFFECTIVE_MODEL' (re-issue /model when the proxy is back)" >&2
|
|
1244
1280
|
printf 'LiteLLM proxy was unreachable at boot, so the session model override `%s` was not applied — the agent booted on its configured default `%s`. Re-issue /model %s once the proxy is reachable.\n' "$_sm_model" "$_EFFECTIVE_MODEL" "$_sm_model" > "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1281
|
+
elif [ "$_sm_attempts" -gt "$_SM_MAX_ATTEMPTS" ]; then
|
|
1282
|
+
# CRASHLOOP BOUND (#3284). This carrier has now been read on more than
|
|
1283
|
+
# _SM_MAX_ATTEMPTS boots WITHOUT the gateway ever reaching boot.lock_acquired
|
|
1284
|
+
# to clear it — i.e. every apply-boot wedged/crashed before a healthy
|
|
1285
|
+
# gateway. That is the signature of a genuinely bad token (one that will
|
|
1286
|
+
# never boot), NOT the transient boot-lock race we retry for. Give up:
|
|
1287
|
+
# drop the carrier + counter, revert to the configured default, and alert.
|
|
1288
|
+
# This is the hard finite bound that guarantees no unbounded crashloop.
|
|
1289
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1290
|
+
echo "session-model: override '$_sm_model' did not reach a healthy boot after $_SM_MAX_ATTEMPTS attempts — reverting to configured default '$_EFFECTIVE_MODEL'" >&2
|
|
1291
|
+
printf 'Your session model override `%s` could not boot successfully after %s attempts, so the agent reverted to its configured default `%s`. Re-issue /model %s to try again.\n' "$_sm_model" "$_SM_MAX_ATTEMPTS" "$_EFFECTIVE_MODEL" "$_sm_model" > "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1245
1292
|
else
|
|
1246
|
-
# Apply the carrier for THIS session
|
|
1247
|
-
#
|
|
1293
|
+
# Apply the carrier for THIS session and KEEP it: the gateway deletes the
|
|
1294
|
+
# carrier + counter once it acquires the boot lock (healthy boot), so a
|
|
1295
|
+
# transient wedge before that RE-APPLIES on the retry boot instead of
|
|
1296
|
+
# reverting. Persist the incremented attempt counter so the crashloop bound
|
|
1297
|
+
# above can fire if this token keeps wedging. No boot alert — the gateway
|
|
1298
|
+
# already acked the /model switch in chat, and the override reverts on the
|
|
1299
|
+
# next restart (once the gateway has cleared the carrier).
|
|
1300
|
+
printf '%s\n' "$_sm_attempts" > "{{agentDir}}/.session-model-boot-attempts" 2>/dev/null || true
|
|
1248
1301
|
_EFFECTIVE_MODEL="$_sm_model"
|
|
1249
|
-
echo "session-model: applying session override '$_sm_model' this boot (
|
|
1302
|
+
echo "session-model: applying session override '$_sm_model' this boot (attempt $_sm_attempts/$_SM_MAX_ATTEMPTS — cleared by the gateway on a healthy boot, reverts on next restart)" >&2
|
|
1250
1303
|
fi
|
|
1251
|
-
unset _smf _sm_model _sm_cfg
|
|
1304
|
+
unset _smf _sm_model _sm_cfg _sm_attempts _SM_MAX_ATTEMPTS
|
|
1252
1305
|
fi
|
|
1253
1306
|
|
|
1254
1307
|
# --- Session effort resolution (consume-once .session-effort carrier, #3186) ---
|
|
@@ -22970,6 +22970,65 @@ import {
|
|
|
22970
22970
|
import { homedir as homedir2 } from "os";
|
|
22971
22971
|
import { basename, join as join3 } from "path";
|
|
22972
22972
|
|
|
22973
|
+
// text-voice-scrub.ts
|
|
22974
|
+
var NULL = "\x00";
|
|
22975
|
+
var FENCE_PH = `${NULL}VS_FENCE`;
|
|
22976
|
+
var INLINE_PH = `${NULL}VS_INLINE`;
|
|
22977
|
+
var HTML_CODE_PH = `${NULL}VS_HTMLCODE`;
|
|
22978
|
+
var HTML_PRE_PH = `${NULL}VS_HTMLPRE`;
|
|
22979
|
+
var URL_PH = `${NULL}VS_URL`;
|
|
22980
|
+
|
|
22981
|
+
// model-unavailable.ts
|
|
22982
|
+
var transientUpstreamSignals = [
|
|
22983
|
+
"not your usage limit",
|
|
22984
|
+
"not your account",
|
|
22985
|
+
"not your account's",
|
|
22986
|
+
"temporarily limiting requests",
|
|
22987
|
+
"temporarily rate",
|
|
22988
|
+
"server is temporarily",
|
|
22989
|
+
"would exceed your account\u2019s rate limit",
|
|
22990
|
+
"would exceed your account's rate limit"
|
|
22991
|
+
];
|
|
22992
|
+
function isTransientUpstreamSignal(text) {
|
|
22993
|
+
if (typeof text !== "string" || text.length === 0)
|
|
22994
|
+
return false;
|
|
22995
|
+
const sample = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
22996
|
+
const lower = sample.toLowerCase();
|
|
22997
|
+
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
22998
|
+
}
|
|
22999
|
+
var litellmProxyLocal429Signals = [
|
|
23000
|
+
"deployment over user-defined ratelimit",
|
|
23001
|
+
"model rate limit exceeded. tpm limit",
|
|
23002
|
+
"model rate limit exceeded. rpm limit",
|
|
23003
|
+
"deployment over defined rpm limit",
|
|
23004
|
+
"no deployments available for selected model",
|
|
23005
|
+
"litellm rate limit handler",
|
|
23006
|
+
"crossed tpm / rpm",
|
|
23007
|
+
"max parallel request limit reached"
|
|
23008
|
+
];
|
|
23009
|
+
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
23010
|
+
function isLitellmProxyLocal429(text) {
|
|
23011
|
+
if (typeof text !== "string" || text.length === 0)
|
|
23012
|
+
return false;
|
|
23013
|
+
const sample = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
23014
|
+
const lower = sample.toLowerCase();
|
|
23015
|
+
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
23016
|
+
return true;
|
|
23017
|
+
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
23018
|
+
}
|
|
23019
|
+
function isLitellmProxyAuthMisconfig(text) {
|
|
23020
|
+
if (typeof text !== "string" || text.length === 0)
|
|
23021
|
+
return false;
|
|
23022
|
+
const sample = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
23023
|
+
const lower = sample.toLowerCase();
|
|
23024
|
+
if (lower.includes("x-api-key header is required"))
|
|
23025
|
+
return true;
|
|
23026
|
+
const isAuthErr = lower.includes("authentication_error") || lower.includes("authenticationerror");
|
|
23027
|
+
if (!isAuthErr)
|
|
23028
|
+
return false;
|
|
23029
|
+
return lower.includes("fallback") && lower.includes("x-api-key");
|
|
23030
|
+
}
|
|
23031
|
+
|
|
22973
23032
|
// operator-events.ts
|
|
22974
23033
|
function classifyClaudeError(raw) {
|
|
22975
23034
|
try {
|
|
@@ -22987,6 +23046,12 @@ function classifyInner(raw) {
|
|
|
22987
23046
|
const message = extractString(obj, "message") ?? extractString(getNestedObj(obj, "error"), "message") ?? (typeof raw === "string" ? raw : "") ?? "";
|
|
22988
23047
|
const status = extractNumber(obj, "status") ?? extractNumber(obj, "statusCode") ?? extractNumber(obj, "status_code") ?? null;
|
|
22989
23048
|
const sdkCode = extractString(obj, "error_code") ?? "";
|
|
23049
|
+
if (isLitellmProxyAuthMisconfig(`${errorType}
|
|
23050
|
+
${errorCode}
|
|
23051
|
+
${sdkCode}
|
|
23052
|
+
${message}`)) {
|
|
23053
|
+
return "proxy-misconfig";
|
|
23054
|
+
}
|
|
22990
23055
|
if (errorType === "authentication_error" || errorCode === "authentication_error" || sdkCode === "authentication_error" || message.toLowerCase().includes("authentication_error")) {
|
|
22991
23056
|
const msg = message.toLowerCase();
|
|
22992
23057
|
if (msg.includes("expired") || msg.includes("refresh")) {
|
|
@@ -23034,53 +23099,12 @@ function getNestedObj(obj, key) {
|
|
|
23034
23099
|
}
|
|
23035
23100
|
var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS = 5 * 60000;
|
|
23036
23101
|
var cooldownMap = new Map;
|
|
23037
|
-
|
|
23038
|
-
|
|
23039
|
-
|
|
23040
|
-
|
|
23041
|
-
|
|
23042
|
-
|
|
23043
|
-
var HTML_PRE_PH = `${NULL}VS_HTMLPRE`;
|
|
23044
|
-
var URL_PH = `${NULL}VS_URL`;
|
|
23045
|
-
|
|
23046
|
-
// model-unavailable.ts
|
|
23047
|
-
var transientUpstreamSignals = [
|
|
23048
|
-
"not your usage limit",
|
|
23049
|
-
"not your account",
|
|
23050
|
-
"not your account's",
|
|
23051
|
-
"temporarily limiting requests",
|
|
23052
|
-
"temporarily rate",
|
|
23053
|
-
"server is temporarily",
|
|
23054
|
-
"would exceed your account\u2019s rate limit",
|
|
23055
|
-
"would exceed your account's rate limit"
|
|
23056
|
-
];
|
|
23057
|
-
function isTransientUpstreamSignal(text) {
|
|
23058
|
-
if (typeof text !== "string" || text.length === 0)
|
|
23059
|
-
return false;
|
|
23060
|
-
const sample = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
23061
|
-
const lower = sample.toLowerCase();
|
|
23062
|
-
return transientUpstreamSignals.some((s) => lower.includes(s));
|
|
23063
|
-
}
|
|
23064
|
-
var litellmProxyLocal429Signals = [
|
|
23065
|
-
"deployment over user-defined ratelimit",
|
|
23066
|
-
"model rate limit exceeded. tpm limit",
|
|
23067
|
-
"model rate limit exceeded. rpm limit",
|
|
23068
|
-
"deployment over defined rpm limit",
|
|
23069
|
-
"no deployments available for selected model",
|
|
23070
|
-
"litellm rate limit handler",
|
|
23071
|
-
"crossed tpm / rpm",
|
|
23072
|
-
"max parallel request limit reached"
|
|
23073
|
-
];
|
|
23074
|
-
var litellmV3LimiterSignalPair = ["rate limit exceeded for ", "limit type:"];
|
|
23075
|
-
function isLitellmProxyLocal429(text) {
|
|
23076
|
-
if (typeof text !== "string" || text.length === 0)
|
|
23077
|
-
return false;
|
|
23078
|
-
const sample = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
23079
|
-
const lower = sample.toLowerCase();
|
|
23080
|
-
if (litellmProxyLocal429Signals.some((s) => lower.includes(s)))
|
|
23081
|
-
return true;
|
|
23082
|
-
return litellmV3LimiterSignalPair.every((s) => lower.includes(s));
|
|
23083
|
-
}
|
|
23102
|
+
var OPERATOR_ACTIONABLE_KINDS = new Set([
|
|
23103
|
+
"credentials-expired",
|
|
23104
|
+
"credentials-invalid",
|
|
23105
|
+
"credit-exhausted",
|
|
23106
|
+
"proxy-misconfig"
|
|
23107
|
+
]);
|
|
23084
23108
|
|
|
23085
23109
|
// tool-label-sidecar.ts
|
|
23086
23110
|
import { existsSync as existsSync2, readFileSync, statSync as statSync2 } from "node:fs";
|