switchroom 0.19.18 → 0.19.19

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.
Files changed (38) hide show
  1. package/dist/agent-scheduler/index.js +2 -1
  2. package/dist/auth-broker/index.js +3 -1
  3. package/dist/cli/drive-write-pretool.mjs +48 -5
  4. package/dist/cli/ms-365-write-pretool.mjs +40 -2
  5. package/dist/cli/notion-write-pretool.mjs +2 -1
  6. package/dist/cli/switchroom.js +3392 -1569
  7. package/dist/host-control/main.js +12209 -11396
  8. package/dist/vault/approvals/kernel-server.js +60 -7
  9. package/dist/vault/broker/server.js +206 -76
  10. package/package.json +4 -3
  11. package/profiles/_base/start.sh.hbs +61 -1
  12. package/telegram-plugin/bridge/bridge.ts +14 -0
  13. package/telegram-plugin/dist/bridge/bridge.js +13 -0
  14. package/telegram-plugin/dist/gateway/gateway.js +1644 -1044
  15. package/telegram-plugin/dist/server.js +13 -0
  16. package/telegram-plugin/gateway/always-allow-persist-queue.ts +97 -11
  17. package/telegram-plugin/gateway/missed-approvals-store.ts +66 -17
  18. package/telegram-plugin/gateway/pending-card-store.ts +46 -16
  19. package/telegram-plugin/gateway/scoped-grant-store.ts +39 -14
  20. package/telegram-plugin/gateway/store-file.ts +244 -0
  21. package/telegram-plugin/hooks/tool-label-pretool.mjs +88 -2
  22. package/telegram-plugin/tests/bridge-tool-parity.test.ts +95 -0
  23. package/telegram-plugin/tests/store-atomic-write.test.ts +411 -0
  24. package/telegram-plugin/tests/tool-activity-summary.test.ts +9 -2
  25. package/telegram-plugin/tests/tool-label-pretool.test.ts +94 -0
  26. package/telegram-plugin/tests/worker-feed-repeat-steps.test.ts +147 -0
  27. package/telegram-plugin/worker-activity-feed.ts +51 -1
  28. package/vendor/hindsight-memory/scripts/drain_pending.py +668 -56
  29. package/vendor/hindsight-memory/scripts/lib/client.py +124 -0
  30. package/vendor/hindsight-memory/scripts/lib/pending.py +865 -33
  31. package/vendor/hindsight-memory/scripts/lib/retain_split.py +449 -0
  32. package/vendor/hindsight-memory/scripts/session_start.py +48 -0
  33. package/vendor/hindsight-memory/scripts/tests/test_client_document_exists.py +470 -0
  34. package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +2121 -0
  35. package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +430 -0
  36. package/vendor/hindsight-memory/scripts/tests/test_session_start_version_skew.py +204 -0
  37. package/vendor/hindsight-memory/tests/test_drain_pending.py +102 -6
  38. package/vendor/hindsight-memory/tests/test_pending.py +32 -7
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.19.18",
4
+ "version": "0.19.19",
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": {
@@ -26,14 +26,15 @@
26
26
  "pretest": "npm run build",
27
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 telegram-plugin/tests/catch-all-forwarded-history.test.ts src/vault/grants.test.ts src/vault/grants-db.test.ts src/vault/write-grants.test.ts src/vault/broker/server-grants.test.ts src/vault/broker/server-write-grants.test.ts src/vault/broker/server-scope-persist.test.ts src/vault/broker/server-tokenless-scope.test.ts src/vault/broker/server-mint-grant-passphrase-attest.test.ts src/vault/broker/server-passphrase-attest.test.ts src/vault/broker/server-mint-grant-posture-attest.test.ts src/vault/broker/server-admin-only-keys.test.ts src/vault/broker/client-token.test.ts src/vault/broker/server-unlock.test.ts src/vault/broker/auto-unlock.test.ts src/vault/broker/drift-detection.test.ts tests/vault-broker-passphrase.test.ts src/cli/vault-get-broker.test.ts src/vault/resolver-via-broker.test.ts src/vault/broker/scope.test.ts src/vault/broker/server.test.ts src/litellm/provision-apply-e2e.test.ts src/drive/disconnect.test.ts src/drive/grants.test.ts src/drive/oauth.test.ts src/drive/onboarding.test.ts src/drive/reconciler.test.ts src/drive/vault-slots.test.ts src/drive/wrapper.test.ts src/vault/approvals/kernel.test.ts src/vault/approvals/approval-origin.test.ts src/vault/approvals/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-scope-persist.test.ts src/vault/broker/server-tokenless-scope.test.ts src/vault/broker/server-mint-grant-passphrase-attest.test.ts src/vault/broker/server-passphrase-attest.test.ts src/vault/broker/server-mint-grant-posture-attest.test.ts src/vault/broker/server-admin-only-keys.test.ts src/vault/broker/client-token.test.ts src/vault/broker/server-unlock.test.ts src/vault/broker/auto-unlock.test.ts src/vault/broker/drift-detection.test.ts tests/vault-broker-passphrase.test.ts src/cli/vault-get-broker.test.ts src/vault/resolver-via-broker.test.ts src/vault/broker/scope.test.ts src/vault/broker/server.test.ts src/litellm/provision-apply-e2e.test.ts src/drive/disconnect.test.ts src/drive/grants.test.ts src/drive/oauth.test.ts src/drive/onboarding.test.ts src/drive/reconciler.test.ts src/drive/vault-slots.test.ts src/drive/wrapper.test.ts src/vault/approvals/kernel.test.ts src/vault/approvals/approval-origin.test.ts src/vault/approvals/self-approval-bypass.test.ts src/vault/approvals/schema-idempotent.test.ts src/vault/broker/server-approvals.test.ts telegram-plugin/tests/boot-probes.test.ts telegram-plugin/tests/boot-version-string.test.ts telegram-plugin/tests/history.test.ts telegram-plugin/tests/cross-turn-card-gate.test.ts telegram-plugin/tests/emission-authority-open-gate.test.ts telegram-plugin/tests/emission-authority-ping-gate.test.ts telegram-plugin/tests/emission-authority-card-drain-gate.test.ts telegram-plugin/tests/per-topic-current-turn.test.ts telegram-plugin/tests/history-reaper.test.ts telegram-plugin/tests/ipc-server-client.test.ts telegram-plugin/tests/ipc-server-race.test.ts telegram-plugin/tests/ipc-server-query-pending-permission.test.ts telegram-plugin/tests/ipc-server-check-pre-approved.test.ts telegram-plugin/tests/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
- "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-mcp-instructions-budget.mjs && node scripts/check-web-subscription-honest.mjs && node scripts/check-no-unpinned-npx-playwright.mjs && node scripts/check-gateway-line-ratchet.mjs && node scripts/check-litellm-config-guard.mjs",
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-auth-test-hermeticity.mjs && node scripts/check-no-broadcast-delivery.mjs && node scripts/check-stale-tool-descriptions.mjs && node scripts/check-mcp-instructions-budget.mjs && node scripts/check-web-subscription-honest.mjs && node scripts/check-no-unpinned-npx-playwright.mjs && node scripts/check-gateway-line-ratchet.mjs && node scripts/check-litellm-config-guard.mjs",
32
32
  "lint:tsc": "tsc --noEmit",
33
33
  "lint:plugin-references": "node scripts/check-plugin-references.mjs",
34
34
  "lint:bot-api-wrapping": "bash scripts/check-bot-api-wrapping.sh",
35
35
  "lint:bun-test-imports": "node scripts/check-bun-test-imports.mjs",
36
36
  "lint:no-pii": "node scripts/check-no-pii-secrets.mjs",
37
+ "lint:auth-test-hermeticity": "node scripts/check-auth-test-hermeticity.mjs",
37
38
  "lint:web-subscription-honest": "node scripts/check-web-subscription-honest.mjs",
38
39
  "lint:no-broadcast-delivery": "node scripts/check-no-broadcast-delivery.mjs",
39
40
  "lint:gateway-line-ratchet": "node scripts/check-gateway-line-ratchet.mjs",
@@ -195,11 +195,33 @@ x-litellm-tags: agent:$SWITCHROOM_AGENT_NAME,profile:${SWITCHROOM_AGENT_PROFILE:
195
195
  # the child keeps appending to the same (now-empty) inode. Worst-case
196
196
  # race is a few log lines written between the cp and the truncate being
197
197
  # lost; acceptable for a debug trace log. Keeps at most ~2×cap on disk
198
- # (live + one .1 generation). Cap + interval are env-overridable.
198
+ # (live + one .1 generation), and since #3596 that .1 is compressed on
199
+ # rotate and age-reaped, so it can't outlive its usefulness. Cap,
200
+ # interval, max-age and compression are all env-overridable.
199
201
  _switchroom_log_rotator() {
200
202
  local _logfile="$1"
201
203
  local _max="${SWITCHROOM_SIDECAR_LOG_MAX_BYTES:-52428800}" # 50 MiB default
202
204
  local _interval="${SWITCHROOM_SIDECAR_LOG_ROTATE_INTERVAL_SEC:-300}" # 5 min
205
+ # #3596: the `.1` generation was written once and then NEVER reaped.
206
+ # A log that grows fast during an incident and slowly afterwards keeps
207
+ # its giant `.1` forever — on the live host clerk/gateway-supervisor.log.1
208
+ # was 582 MB (dated Jul 11) while the live log sat at ~5 MB, i.e. months
209
+ # away from ever being overwritten by the next rotation. Two bounds:
210
+ # * compress on rotate (gzip typically takes a text log to <10%), and
211
+ # * an AGE bound — a `.1`/`.1.gz` older than this is deleted on the
212
+ # next check even if no rotation happens.
213
+ # Be honest about what the age bound COSTS (#3600 review): right after
214
+ # a rotation the `.1` holds everything and the live log is empty, so
215
+ # if the agent then goes quiet for 14 days that history is DELETED
216
+ # while the near-empty live log survives. This is deliberate for a
217
+ # debug trace log — bounding shared host disk beats retaining stale
218
+ # traces — but it is deletion of the only copy, not merely eviction of
219
+ # a superseded backup. Raise SWITCHROOM_SIDECAR_LOG_ROTATE_MAX_AGE_SEC
220
+ # (or set it to 0 to disable the reaper) where that matters. Agent
221
+ # AUDIT trails are not affected: those are the hostd/vault logs, which
222
+ # rotate by count, never by age.
223
+ local _max_age_sec="${SWITCHROOM_SIDECAR_LOG_ROTATE_MAX_AGE_SEC:-1209600}" # 14 days
224
+ local _compress="${SWITCHROOM_SIDECAR_LOG_COMPRESS:-1}"
203
225
  # Validate BOTH env overrides up front; garbage falls back to the
204
226
  # default. Unvalidated, a non-numeric interval makes `sleep` fail
205
227
  # instantly and the `while true` loop hot-spins a CPU core for the
@@ -208,12 +230,28 @@ x-litellm-tags: agent:$SWITCHROOM_AGENT_NAME,profile:${SWITCHROOM_AGENT_PROFILE:
208
230
  # interval would also hot-spin, so it falls back too.
209
231
  case "$_max" in ''|*[!0-9]*) _max=52428800;; esac
210
232
  case "$_interval" in ''|0|*[!0-9]*) _interval=300;; esac
233
+ # Same garbage guard for the age bound; 0 disables the age reaper
234
+ # (the size-triggered overwrite still applies).
235
+ case "$_max_age_sec" in ''|*[!0-9]*) _max_age_sec=1209600;; esac
236
+ # `find -mmin` takes MINUTES; round up so a sub-minute age still reaps.
237
+ local _max_age_min=$(( (_max_age_sec + 59) / 60 ))
211
238
  # A cap of 0 disables rotation entirely (operator escape hatch).
212
239
  # (Negative values contain '-' → non-numeric per the guard above →
213
240
  # default cap, i.e. rotation stays on.)
214
241
  [ "$_max" -eq 0 ] && return 0
242
+ local _dir _base
243
+ _dir=$(dirname "$_logfile")
244
+ _base=$(basename "$_logfile")
215
245
  while true; do
216
246
  sleep "$_interval"
247
+ # Age-reap the retained generation first — this runs every cycle,
248
+ # INDEPENDENT of whether the live log is over cap, which is exactly
249
+ # the case the old code never handled (#3596).
250
+ if [ "$_max_age_min" -gt 0 ]; then
251
+ find "$_dir" -maxdepth 1 -type f \
252
+ \( -name "$_base.1" -o -name "$_base.1.gz" \) \
253
+ -mmin "+$_max_age_min" -exec rm -f {} + 2>/dev/null || true
254
+ fi
217
255
  [ -f "$_logfile" ] || continue
218
256
  local _size
219
257
  _size=$(wc -c < "$_logfile" 2>/dev/null || echo 0)
@@ -222,6 +260,13 @@ x-litellm-tags: agent:$SWITCHROOM_AGENT_NAME,profile:${SWITCHROOM_AGENT_PROFILE:
222
260
  if [ "$_size" -gt "$_max" ]; then
223
261
  if cp "$_logfile" "$_logfile.1" 2>/dev/null; then
224
262
  : > "$_logfile"
263
+ # Drop any stale compressed generation, then (best-effort)
264
+ # compress the fresh one. gzip failing (missing binary, ENOSPC)
265
+ # just leaves the plain `.1` — never fatal.
266
+ rm -f "$_logfile.1.gz" 2>/dev/null || true
267
+ if [ "$_compress" != "0" ] && command -v gzip >/dev/null 2>&1; then
268
+ gzip -f "$_logfile.1" 2>/dev/null || true
269
+ fi
225
270
  echo "[supervise] rotated $_logfile (${_size} bytes > ${_max} cap) → ${_logfile}.1" >> "$_logfile"
226
271
  else
227
272
  # cp failed (e.g. ENOSPC — exactly when rotation matters most).
@@ -1104,6 +1149,21 @@ if [ -f /opt/switchroom/webkite/config.toml ]; then
1104
1149
  unset sr_wk_target
1105
1150
  fi
1106
1151
 
1152
+ # Shared cloakbrowser Chromium cache (#TBD). The image pins
1153
+ # CLOAKBROWSER_CACHE_DIR to /opt/switchroom/cloakbrowser-cache and
1154
+ # compose bind-mounts the operator's single ~/.switchroom/cloakbrowser/
1155
+ # copy RO onto it. If that mount didn't materialise the dir is empty and
1156
+ # root-owned, so cloakbrowser cannot silently re-download its own ~700MB
1157
+ # private copy (verified live: it aborts with `[Errno 13] Permission
1158
+ # denied` instead of extracting) — it only loses local render. Warn
1159
+ # loudly, never fatally: webkite still cloud-renders when the
1160
+ # Cloudflare/Firecrawl credentials are present.
1161
+ sr_cb_dir="${CLOAKBROWSER_CACHE_DIR:-/opt/switchroom/cloakbrowser-cache}"
1162
+ if ! ls -d "$sr_cb_dir"/chromium-*/ >/dev/null 2>&1; then
1163
+ echo "WARNING (cloakbrowser shared cache): shared cloakbrowser Chromium missing at $sr_cb_dir — webkite local render unavailable (cloud render still works if Cloudflare/Firecrawl creds are set). On the host run: CLOAKBROWSER_CACHE_DIR=~/.switchroom/cloakbrowser cloakbrowser install && switchroom apply" >&2
1164
+ fi
1165
+ unset sr_cb_dir
1166
+
1107
1167
  # LiteLLM routing (opt-in, #litellm). When SWITCHROOM_LITELLM is set (compose
1108
1168
  # env, gated on litellm.enabled && keyConfirmed), route the unmodified `claude`
1109
1169
  # CLI through the operator's LiteLLM proxy at ANTHROPIC_BASE_URL: fetch the
@@ -133,6 +133,20 @@ const TOOL_SCHEMAS = [
133
133
  required: ['chat_id', 'text'],
134
134
  },
135
135
  },
136
+ {
137
+ name: 'progress_update',
138
+ description:
139
+ 'Post a short interim progress line to Telegram mid-task ("still working through X"). Sends a NEW plain message to the chat — it is not an edit and not a card row, so use it sparingly and only when the user genuinely benefits from knowing where a long task stands. The gateway enforces its own limits: text is truncated at 300 chars, at most one update per 20s per chat+thread, and at most 5 per turn; over-limit calls return {ok:false, reason:"too_soon"|"turn_limit"} instead of sending. Prefer edit_message when you already own a message to update, and always deliver the actual answer with reply.',
140
+ inputSchema: {
141
+ type: 'object',
142
+ properties: {
143
+ chat_id: { type: 'string', description: 'Chat to post the progress line in — pass chat_id from the inbound message.' },
144
+ text: { type: 'string', description: 'The progress line. One short sentence; truncated at 300 chars by the gateway.' },
145
+ message_thread_id: { type: 'string', description: 'Forum topic thread ID. Auto-applied from the last inbound message in the same chat if not specified.' },
146
+ },
147
+ required: ['chat_id', 'text'],
148
+ },
149
+ },
136
150
  {
137
151
  name: 'react',
138
152
  description: 'Add an emoji reaction to a Telegram message. Telegram only accepts a fixed whitelist (👍 👎 ❤ 🔥 👀 🎉 etc) — non-whitelisted emoji will be rejected.',
@@ -25004,6 +25004,19 @@ var TOOL_SCHEMAS = [
25004
25004
  required: ["chat_id", "text"]
25005
25005
  }
25006
25006
  },
25007
+ {
25008
+ name: "progress_update",
25009
+ description: 'Post a short interim progress line to Telegram mid-task ("still working through X"). Sends a NEW plain message to the chat \u2014 it is not an edit and not a card row, so use it sparingly and only when the user genuinely benefits from knowing where a long task stands. The gateway enforces its own limits: text is truncated at 300 chars, at most one update per 20s per chat+thread, and at most 5 per turn; over-limit calls return {ok:false, reason:"too_soon"|"turn_limit"} instead of sending. Prefer edit_message when you already own a message to update, and always deliver the actual answer with reply.',
25010
+ inputSchema: {
25011
+ type: "object",
25012
+ properties: {
25013
+ chat_id: { type: "string", description: "Chat to post the progress line in \u2014 pass chat_id from the inbound message." },
25014
+ text: { type: "string", description: "The progress line. One short sentence; truncated at 300 chars by the gateway." },
25015
+ message_thread_id: { type: "string", description: "Forum topic thread ID. Auto-applied from the last inbound message in the same chat if not specified." }
25016
+ },
25017
+ required: ["chat_id", "text"]
25018
+ }
25019
+ },
25007
25020
  {
25008
25021
  name: "react",
25009
25022
  description: "Add an emoji reaction to a Telegram message. Telegram only accepts a fixed whitelist (\uD83D\uDC4D \uD83D\uDC4E \u2764 \uD83D\uDD25 \uD83D\uDC40 \uD83C\uDF89 etc) \u2014 non-whitelisted emoji will be rejected.",