switchroom 0.18.29 → 0.18.31
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 +2089 -1587
- 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 +172 -22
- package/telegram-plugin/dist/bridge/bridge.js +71 -47
- package/telegram-plugin/dist/gateway/gateway.js +601 -104
- package/telegram-plugin/dist/server.js +89 -64
- package/telegram-plugin/gateway/gateway.ts +280 -33
- package/telegram-plugin/gateway/model-command.ts +104 -0
- package/telegram-plugin/gateway/session-model-file.ts +40 -0
- package/telegram-plugin/gateway/turn-flush-suppression.ts +82 -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/telegram-plugin/tests/turn-flush-suppression.test.ts +90 -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.31",
|
|
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",
|
|
@@ -1170,22 +1170,106 @@ fi
|
|
|
1170
1170
|
# which the gateway relays to the operator chat once at boot. The normal apply
|
|
1171
1171
|
# path is silent here — the gateway already acked the `/model` in chat.
|
|
1172
1172
|
#
|
|
1173
|
+
# --- Live configured-default resolution (Defect B / drift fix) ---
|
|
1174
|
+
#
|
|
1175
|
+
# The apply-time bake ({{{modelQ}}}) is only the LAST-DITCH fallback: editing
|
|
1176
|
+
# `model:` in switchroom.yaml and restarting must take effect WITHOUT a full
|
|
1177
|
+
# `switchroom apply`. The launcher asks the SAME resolver the gateway's
|
|
1178
|
+
# /status uses (`switchroom agent effective-model`, which applies
|
|
1179
|
+
# resolveMainModel server-side) against the live mounted yaml — one resolver,
|
|
1180
|
+
# never re-implemented in shell, so what-you-see and what-runs cannot split.
|
|
1181
|
+
#
|
|
1182
|
+
# Fallback chain, never silent past step 1:
|
|
1183
|
+
# 1. live read of $SWITCHROOM_CONFIG (one bounded retry — a host-side
|
|
1184
|
+
# in-place rewrite can present a torn file for a moment);
|
|
1185
|
+
# 2. `.configured-default-model` — the last-known-good live value this
|
|
1186
|
+
# launcher recorded on every prior boot (read BEFORE it is overwritten);
|
|
1187
|
+
# 3. the apply-time bake.
|
|
1188
|
+
# Steps 2/3 append to `.session-model-alert` so the gateway tells the
|
|
1189
|
+
# operator once at boot. No SWITCHROOM_CONFIG mount at all means there is
|
|
1190
|
+
# nothing to read live — the bake IS the truth there (stderr note only; an
|
|
1191
|
+
# operator alert every boot forever would be noise with no action).
|
|
1192
|
+
#
|
|
1193
|
+
# The yaml file bind mount re-resolves by PATH at container start, so a
|
|
1194
|
+
# host-side atomic-rename write is always picked up by the NEXT boot — the
|
|
1195
|
+
# path this block runs on. Only mid-life reads by long-running processes can
|
|
1196
|
+
# see a stale inode; that pre-existing edge is out of scope here (tracked as
|
|
1197
|
+
# a follow-up on the drift fix).
|
|
1198
|
+
#
|
|
1173
1199
|
# NB {{{modelQ}}} is already shell-single-quoted by the scaffold (it renders as
|
|
1174
1200
|
# a quoted token, e.g. 'claude-sonnet-5'), so it is assigned BARE here — never
|
|
1175
1201
|
# inside additional double quotes, which would embed the literal quote chars in
|
|
1176
1202
|
# the value and break `claude --model`.
|
|
1177
|
-
|
|
1203
|
+
_BAKED_MODEL={{{modelQ}}}
|
|
1204
|
+
_LKG_MODEL="$(cat "{{agentDir}}/.configured-default-model" 2>/dev/null | tr -d '[:space:]' || true)"
|
|
1205
|
+
_EFFECTIVE_MODEL=""
|
|
1206
|
+
if [ -n "$SWITCHROOM_CONFIG" ] && [ -f "$SWITCHROOM_CONFIG" ] && command -v switchroom >/dev/null 2>&1; then
|
|
1207
|
+
_lm_live=""
|
|
1208
|
+
for _lm_try in 1 2; do
|
|
1209
|
+
_lm_live="$(timeout 20 switchroom --config "$SWITCHROOM_CONFIG" agent effective-model "{{name}}" 2>/dev/null | tail -n 1 | tr -d '[:space:]')" || _lm_live=""
|
|
1210
|
+
# Shape gate — same regex as the .session-model carrier below (byte-kept
|
|
1211
|
+
# with MODEL_ARG_RE); the tr above already guarantees single-line.
|
|
1212
|
+
if [ -n "$_lm_live" ] && printf '%s' "$_lm_live" | grep -Eq '^[A-Za-z0-9][]A-Za-z0-9._[/-]{0,99}$'; then
|
|
1213
|
+
break
|
|
1214
|
+
fi
|
|
1215
|
+
_lm_live=""
|
|
1216
|
+
if [ "$_lm_try" -eq 1 ]; then sleep 1; fi
|
|
1217
|
+
done
|
|
1218
|
+
if [ -n "$_lm_live" ]; then
|
|
1219
|
+
# Routing class (Claude passthrough vs sr-* router root) is baked into
|
|
1220
|
+
# compose at render time, so a Claude<->sr-* class flip CANNOT be applied
|
|
1221
|
+
# by a bare restart — half-applying would run the new model against the
|
|
1222
|
+
# old routing. Detect the flip, keep the baked model, and tell the
|
|
1223
|
+
# operator to run a real apply.
|
|
1224
|
+
case "$_BAKED_MODEL" in sr-*) _lm_baked_class=router ;; *) _lm_baked_class=claude ;; esac
|
|
1225
|
+
case "$_lm_live" in sr-*) _lm_live_class=router ;; *) _lm_live_class=claude ;; esac
|
|
1226
|
+
if [ "$_lm_live_class" != "$_lm_baked_class" ]; then
|
|
1227
|
+
echo "session-model: configured model class changed ('$_BAKED_MODEL' -> '$_lm_live', $_lm_baked_class -> $_lm_live_class) — routing is render-time, NOT applying; run 'switchroom apply' then restart" >&2
|
|
1228
|
+
printf 'The configured model changed from `%s` to `%s`, which switches routing class — a restart alone cannot apply that. The agent booted on `%s`. Run `switchroom apply` on the host, then restart this agent.\n' "$_BAKED_MODEL" "$_lm_live" "$_BAKED_MODEL" >> "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1229
|
+
_EFFECTIVE_MODEL="$_BAKED_MODEL"
|
|
1230
|
+
else
|
|
1231
|
+
_EFFECTIVE_MODEL="$_lm_live"
|
|
1232
|
+
if [ "$_lm_live" != "$_BAKED_MODEL" ]; then
|
|
1233
|
+
echo "session-model: live configured model '$_lm_live' differs from apply-time bake '$_BAKED_MODEL' — using the live value (switchroom.yaml is the source of truth)" >&2
|
|
1234
|
+
fi
|
|
1235
|
+
fi
|
|
1236
|
+
elif [ -n "$_LKG_MODEL" ] && printf '%s' "$_LKG_MODEL" | grep -Eq '^[A-Za-z0-9][]A-Za-z0-9._[/-]{0,99}$'; then
|
|
1237
|
+
# Live read failed (torn write mid-read, yaml schema error, CLI wedge).
|
|
1238
|
+
# Last-known-good beats the bake: it was live truth on a prior boot,
|
|
1239
|
+
# while the bake can predate any number of yaml edits. Loud either way —
|
|
1240
|
+
# a silent fallback here would resurrect Defect B as an intermittent flap.
|
|
1241
|
+
_EFFECTIVE_MODEL="$_LKG_MODEL"
|
|
1242
|
+
echo "session-model: live config read FAILED — using last-known-good configured model '$_LKG_MODEL' (apply-time bake: '$_BAKED_MODEL')" >&2
|
|
1243
|
+
printf 'switchroom.yaml could not be read at boot, so the agent booted on the last-known-good configured model `%s`. If you just changed `model:`, check the yaml for errors and restart the agent.\n' "$_LKG_MODEL" >> "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1244
|
+
else
|
|
1245
|
+
_EFFECTIVE_MODEL="$_BAKED_MODEL"
|
|
1246
|
+
echo "session-model: live config read FAILED and no last-known-good value — using apply-time baked model '$_BAKED_MODEL'" >&2
|
|
1247
|
+
printf 'switchroom.yaml could not be read at boot and no prior boot recorded a model, so the agent booted on the apply-time default `%s`. If you just changed `model:`, check the yaml for errors and restart the agent.\n' "$_BAKED_MODEL" >> "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1248
|
+
fi
|
|
1249
|
+
unset _lm_live _lm_try _lm_baked_class _lm_live_class
|
|
1250
|
+
else
|
|
1251
|
+
_EFFECTIVE_MODEL="$_BAKED_MODEL"
|
|
1252
|
+
fi
|
|
1253
|
+
unset _BAKED_MODEL _LKG_MODEL
|
|
1178
1254
|
# Record the RESOLVED configured default (raw, unquoted) every boot, before
|
|
1179
1255
|
# override resolution. The gateway copies this into the carrier's
|
|
1180
1256
|
# `configuredDefaultAtWrite`, so both sides of the invalidation compare below
|
|
1181
|
-
# come from the same resolver. Overwrite, not consumed
|
|
1257
|
+
# come from the same resolver. Overwrite, not consumed — and doubling as the
|
|
1258
|
+
# last-known-good live value the fallback chain above reads on the NEXT boot.
|
|
1182
1259
|
printf '%s\n' "$_EFFECTIVE_MODEL" > "{{agentDir}}/.configured-default-model" 2>/dev/null || true
|
|
1183
1260
|
|
|
1184
1261
|
# 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-
|
|
1262
|
+
# keep/revert intent subsystem. Nothing reads them anymore; without this rm they
|
|
1263
|
+
# would linger in the bind-mounted state dir forever after rollout. Idempotent —
|
|
1264
|
+
# a cheap no-op once clean.
|
|
1265
|
+
rm -f "{{agentDir}}/.relaunch-model-intent" "{{agentDir}}/.session-model-kept-notified"
|
|
1266
|
+
# `.session-model-boot-attempts` is REVIVED as LIVE state (#3284 bounded-retry
|
|
1267
|
+
# counter, see the resolution block below): the gateway clears it on a healthy
|
|
1268
|
+
# boot, and start.sh gives up + reverts once it exceeds the attempt bound. It is
|
|
1269
|
+
# meaningless without a carrier to belong to, so sweep it as stale ONLY when no
|
|
1270
|
+
# `.session-model` carrier is present (rollout leftover / completed apply / a
|
|
1271
|
+
# prior giveup that didn't get to delete it).
|
|
1272
|
+
[ -f "{{agentDir}}/.session-model" ] || rm -f "{{agentDir}}/.session-model-boot-attempts"
|
|
1189
1273
|
|
|
1190
1274
|
# Migration shim (one release): a leftover one-shot `.session-model-override`
|
|
1191
1275
|
# carrier from a pre-consume-once gateway means an OLD gateway wrote it
|
|
@@ -1207,11 +1291,34 @@ if [ -f "{{agentDir}}/.session-model" ]; then
|
|
|
1207
1291
|
_smf="$(cat "{{agentDir}}/.session-model" 2>/dev/null || true)"
|
|
1208
1292
|
_sm_model="$(printf '%s' "$_smf" | sed -n 's/.*"model"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
|
1209
1293
|
_sm_cfg="$(printf '%s' "$_smf" | sed -n 's/.*"configuredDefaultAtWrite"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
|
1210
|
-
#
|
|
1211
|
-
#
|
|
1212
|
-
#
|
|
1213
|
-
#
|
|
1214
|
-
|
|
1294
|
+
# BOUNDED-RETRY CONSUME (#3284) — the carrier is NO LONGER deleted here before
|
|
1295
|
+
# the apply/exec. It used to be (pure consume-once), but a boot that reads the
|
|
1296
|
+
# carrier may WEDGE before its gateway acquires the boot lock (the
|
|
1297
|
+
# boot.lock_stale_recovered_boot_mismatch race, e.g. a proxy-only `fable`
|
|
1298
|
+
# apply-boot) and never become the surviving healthy session. Deleting before
|
|
1299
|
+
# apply handed the requested model to that loser: the retry boot found no
|
|
1300
|
+
# carrier and silently reverted to the configured default. Instead:
|
|
1301
|
+
#
|
|
1302
|
+
# - the GATEWAY deletes the carrier + the attempt counter once it ACQUIRES
|
|
1303
|
+
# the boot lock (boot.lock_acquired in gateway.ts — the healthy-boot signal
|
|
1304
|
+
# a wedged boot cannot fake), so a GOOD token survives a transient wedge
|
|
1305
|
+
# and RE-APPLIES on the retry boot instead of reverting;
|
|
1306
|
+
# - a persisted attempt counter (`.session-model-boot-attempts`) BOUNDS the
|
|
1307
|
+
# retries: a token that never reaches a healthy gateway (a genuinely BAD
|
|
1308
|
+
# token that crashes claude before lock-acquire) is given up after
|
|
1309
|
+
# _SM_MAX_ATTEMPTS boots — deleted, reverted, and alerted — so a bad token
|
|
1310
|
+
# can NEVER produce an unbounded wedge→retry→wedge crashloop. The original
|
|
1311
|
+
# "a bad token crashes at most one boot then reverts" guarantee becomes "at
|
|
1312
|
+
# most _SM_MAX_ATTEMPTS boots then reverts", still a hard finite bound.
|
|
1313
|
+
#
|
|
1314
|
+
# Every INVALIDATION / GIVEUP branch below deletes BOTH the carrier and the
|
|
1315
|
+
# counter; only the APPLY branch keeps the carrier (for the gateway to clear)
|
|
1316
|
+
# and persists the incremented counter. The delete-before-apply that used to
|
|
1317
|
+
# sit here is GONE by design.
|
|
1318
|
+
_SM_MAX_ATTEMPTS=3
|
|
1319
|
+
_sm_attempts="$(cat "{{agentDir}}/.session-model-boot-attempts" 2>/dev/null | tr -dc '0-9' || true)"
|
|
1320
|
+
[ -z "$_sm_attempts" ] && _sm_attempts=0
|
|
1321
|
+
_sm_attempts=$(( _sm_attempts + 1 ))
|
|
1215
1322
|
# Shape gate — kept BYTE-IDENTICAL with MODEL_ARG_RE in
|
|
1216
1323
|
# telegram-plugin/gateway/model-command.ts. `/` is allowed for
|
|
1217
1324
|
# OpenRouter-style `sr-vendor/model` ids; it is not a shell metachar inside
|
|
@@ -1222,35 +1329,78 @@ if [ -f "{{agentDir}}/.session-model" ]; then
|
|
|
1222
1329
|
# `claude --model` (parity with parseSessionModel's single-string check).
|
|
1223
1330
|
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
1331
|
# Invalid carrier: fall back to the configured default AND tell the
|
|
1225
|
-
# operator once — never a silent stderr-only drop.
|
|
1332
|
+
# operator once — never a silent stderr-only drop. Terminal → drop the
|
|
1333
|
+
# carrier + counter (a corrupt token is never worth retrying).
|
|
1334
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1226
1335
|
echo "session-model: ignoring malformed .session-model (failed shape gate) — using configured default '$_EFFECTIVE_MODEL'" >&2
|
|
1227
1336
|
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
1337
|
elif [ "$_sm_cfg" != "$_EFFECTIVE_MODEL" ]; then
|
|
1229
1338
|
# switchroom.yaml `model:` changed between the switch and this apply-boot →
|
|
1230
1339
|
# the carrier is against a default that no longer exists. Invalidate +
|
|
1231
|
-
# announce once.
|
|
1340
|
+
# announce once. Terminal → drop the carrier + counter.
|
|
1341
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1232
1342
|
echo "session-model: configured default changed ('$_sm_cfg' → '$_EFFECTIVE_MODEL') — dropping session override '$_sm_model'" >&2
|
|
1233
1343
|
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
1344
|
elif { [ "${_sm_model#sr-}" != "$_sm_model" ] || [ "$_sm_model" = fable ] || [ "$_sm_model" = claude-fable-5 ]; } && [ -z "$_LITELLM_OK" ]; then
|
|
1235
1345
|
# Proxy-only override + LiteLLM unreachable at the apply-boot: booting the
|
|
1236
1346
|
# override would 4xx (sr-* against Anthropic; `fable`/`claude-fable-5` is a
|
|
1237
1347
|
# retired codename direct-to-Anthropic and ONLY resolves via the LiteLLM
|
|
1238
|
-
# router — see the repoint case below).
|
|
1239
|
-
#
|
|
1240
|
-
#
|
|
1241
|
-
#
|
|
1242
|
-
# the
|
|
1348
|
+
# router — see the repoint case below). The litellm probe already waited out
|
|
1349
|
+
# its full 120s window before we got here, so "still unreachable" is a real
|
|
1350
|
+
# down-state, not the transient boot-lock wedge the bounded-retry counter
|
|
1351
|
+
# covers — drop the carrier + counter (terminal) and tell the operator to
|
|
1352
|
+
# re-issue once the proxy is back, rather than looping against a dead proxy.
|
|
1353
|
+
# (Rev 5: Fable is now reachable via the carrier relaunch — the old
|
|
1354
|
+
# live-inject path never launched it — so it needs the same down-guard as sr-*.)
|
|
1355
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1243
1356
|
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
1357
|
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
|
|
1358
|
+
elif [ "$_sm_attempts" -gt "$_SM_MAX_ATTEMPTS" ]; then
|
|
1359
|
+
# CRASHLOOP BOUND (#3284). This carrier has now been read on more than
|
|
1360
|
+
# _SM_MAX_ATTEMPTS boots WITHOUT the gateway ever reaching boot.lock_acquired
|
|
1361
|
+
# to clear it — i.e. every apply-boot wedged/crashed before a healthy
|
|
1362
|
+
# gateway. That is the signature of a genuinely bad token (one that will
|
|
1363
|
+
# never boot), NOT the transient boot-lock race we retry for. Give up:
|
|
1364
|
+
# drop the carrier + counter, revert to the configured default, and alert.
|
|
1365
|
+
# This is the hard finite bound that guarantees no unbounded crashloop.
|
|
1366
|
+
rm -f "{{agentDir}}/.session-model" "{{agentDir}}/.session-model-boot-attempts"
|
|
1367
|
+
echo "session-model: override '$_sm_model' did not reach a healthy boot after $_SM_MAX_ATTEMPTS attempts — reverting to configured default '$_EFFECTIVE_MODEL'" >&2
|
|
1368
|
+
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
1369
|
else
|
|
1246
|
-
# Apply the carrier for THIS session
|
|
1247
|
-
#
|
|
1370
|
+
# Apply the carrier for THIS session and KEEP it: the gateway deletes the
|
|
1371
|
+
# carrier + counter once it acquires the boot lock (healthy boot), so a
|
|
1372
|
+
# transient wedge before that RE-APPLIES on the retry boot instead of
|
|
1373
|
+
# reverting. Persist the incremented attempt counter so the crashloop bound
|
|
1374
|
+
# above can fire if this token keeps wedging. No boot alert — the gateway
|
|
1375
|
+
# already acked the /model switch in chat, and the override reverts on the
|
|
1376
|
+
# next restart (once the gateway has cleared the carrier).
|
|
1377
|
+
printf '%s\n' "$_sm_attempts" > "{{agentDir}}/.session-model-boot-attempts" 2>/dev/null || true
|
|
1248
1378
|
_EFFECTIVE_MODEL="$_sm_model"
|
|
1249
|
-
echo "session-model: applying session override '$_sm_model' this boot (
|
|
1379
|
+
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
1380
|
fi
|
|
1251
|
-
unset _smf _sm_model _sm_cfg
|
|
1381
|
+
unset _smf _sm_model _sm_cfg _sm_attempts _SM_MAX_ATTEMPTS
|
|
1252
1382
|
fi
|
|
1253
1383
|
|
|
1384
|
+
# Configured-default LiteLLM guard. A proxy-only *configured* model with
|
|
1385
|
+
# LiteLLM unreachable at boot would exec against the /anthropic passthrough,
|
|
1386
|
+
# where `fable`/`claude-fable-5` is a retired codename and EVERY call 4xxs —
|
|
1387
|
+
# a total outage. The carrier block above already drops proxy-only OVERRIDES
|
|
1388
|
+
# on a down proxy; this extends the same guard to the configured default,
|
|
1389
|
+
# which the live-config read above makes a mainline path (pinning `model:
|
|
1390
|
+
# fable` in yaml is now the normal way to run Fable). Claude agents have a
|
|
1391
|
+
# safe same-class fallback (opus); sr-* configured defaults keep today's
|
|
1392
|
+
# loud-degraded behavior — there is no Claude fallback for them and the
|
|
1393
|
+
# repoint case below already leaves their routing to self-heal.
|
|
1394
|
+
case "$_EFFECTIVE_MODEL" in
|
|
1395
|
+
fable|claude-fable-5)
|
|
1396
|
+
if [ -z "$_LITELLM_OK" ]; then
|
|
1397
|
+
echo "session-model: configured model '$_EFFECTIVE_MODEL' is proxy-only but LiteLLM is unreachable at boot — booting 'opus' until the proxy recovers" >&2
|
|
1398
|
+
printf 'LiteLLM was unreachable at boot, so the configured model `%s` (proxy-only) could not be used — the agent booted on `opus` instead. Restart the agent once the proxy is back to return to `%s`.\n' "$_EFFECTIVE_MODEL" "$_EFFECTIVE_MODEL" >> "{{agentDir}}/.session-model-alert" 2>/dev/null || true
|
|
1399
|
+
_EFFECTIVE_MODEL="opus"
|
|
1400
|
+
fi
|
|
1401
|
+
;;
|
|
1402
|
+
esac
|
|
1403
|
+
|
|
1254
1404
|
# --- Session effort resolution (consume-once .session-effort carrier, #3186) ---
|
|
1255
1405
|
#
|
|
1256
1406
|
# The `/effort` sibling of the block above, session-scoped like /model
|
|
@@ -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";
|