switchroom 0.18.12 → 0.18.14

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 (76) hide show
  1. package/dist/agent-scheduler/index.js +57 -9
  2. package/dist/auth-broker/index.js +174 -72
  3. package/dist/cli/autoaccept-poll.js +23 -0
  4. package/dist/cli/drive-write-pretool.mjs +24 -1
  5. package/dist/cli/foreground-hog-pretool.mjs +264 -0
  6. package/dist/cli/ms-365-write-pretool.mjs +31 -8
  7. package/dist/cli/notion-write-pretool.mjs +9 -2
  8. package/dist/cli/skill-validate-pretool.mjs +144 -2847
  9. package/dist/cli/switchroom.js +986 -3131
  10. package/dist/host-control/main.js +216 -2863
  11. package/dist/vault/approvals/kernel-server.js +67 -1
  12. package/dist/vault/broker/server.js +98 -45
  13. package/package.json +1 -1
  14. package/profiles/coding/CLAUDE.md.hbs +2 -0
  15. package/profiles/default/CLAUDE.md.hbs +2 -0
  16. package/skills/switchroom-architecture/telegram.md +0 -1
  17. package/telegram-plugin/auth-snapshot-format.ts +37 -5
  18. package/telegram-plugin/auto-fallback-fleet.ts +29 -1
  19. package/telegram-plugin/bridge/bridge.ts +2 -0
  20. package/telegram-plugin/dist/bridge/bridge.js +51 -3
  21. package/telegram-plugin/dist/gateway/gateway.js +1251 -2368
  22. package/telegram-plugin/dist/server.js +67 -3
  23. package/telegram-plugin/format.ts +19 -0
  24. package/telegram-plugin/gateway/approval-hold.ts +21 -2
  25. package/telegram-plugin/gateway/auth-broker-client.ts +1 -0
  26. package/telegram-plugin/gateway/auth-command.ts +14 -0
  27. package/telegram-plugin/gateway/callback-query-handlers.ts +12 -0
  28. package/telegram-plugin/gateway/forward-origin.ts +235 -0
  29. package/telegram-plugin/gateway/gateway.ts +445 -83
  30. package/telegram-plugin/gateway/throttle-tier-wiring.ts +268 -0
  31. package/telegram-plugin/history.ts +106 -6
  32. package/telegram-plugin/inline-keyboard-callbacks.ts +94 -0
  33. package/telegram-plugin/model-unavailable.ts +61 -13
  34. package/telegram-plugin/outbound-field-redact.ts +69 -0
  35. package/telegram-plugin/render/render.ts +32 -14
  36. package/telegram-plugin/render/rich-render.ts +40 -32
  37. package/telegram-plugin/scoped-approval.ts +11 -2
  38. package/telegram-plugin/secret-detect/chunker.ts +18 -4
  39. package/telegram-plugin/secret-detect/index.ts +12 -56
  40. package/telegram-plugin/send-gate-degraded.test.ts +131 -0
  41. package/telegram-plugin/send-gate.test.ts +25 -6
  42. package/telegram-plugin/send-gate.ts +82 -8
  43. package/telegram-plugin/session-tail.ts +82 -7
  44. package/telegram-plugin/stream-controller.ts +3 -2
  45. package/telegram-plugin/subagent-watcher.ts +71 -16
  46. package/telegram-plugin/tests/approval-hold-outcome.test.ts +36 -5
  47. package/telegram-plugin/tests/auto-fallback-fleet.test.ts +72 -0
  48. package/telegram-plugin/tests/callback-query-handlers.test.ts +65 -0
  49. package/telegram-plugin/tests/forward-origin.test.ts +309 -0
  50. package/telegram-plugin/tests/gateway-outbound-redact.test.ts +57 -0
  51. package/telegram-plugin/tests/history.test.ts +272 -0
  52. package/telegram-plugin/tests/inbound-message-types.test.ts +5 -1
  53. package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +164 -0
  54. package/telegram-plugin/tests/operator-events-session-tail.test.ts +74 -0
  55. package/telegram-plugin/tests/outbound-field-redact.test.ts +107 -0
  56. package/telegram-plugin/tests/reaction-gate-routing.test.ts +173 -0
  57. package/telegram-plugin/tests/render/render-outbound-chunks.test.ts +6 -4
  58. package/telegram-plugin/tests/render/render.test.ts +88 -0
  59. package/telegram-plugin/tests/render/rich-render.test.ts +41 -22
  60. package/telegram-plugin/tests/scoped-approval.test.ts +27 -0
  61. package/telegram-plugin/tests/secret-detect-chunk-overlap.test.ts +65 -0
  62. package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +5 -4
  63. package/telegram-plugin/tests/session-tail-sidecar-reap.test.ts +268 -0
  64. package/telegram-plugin/tests/single-mode-stream-reply.test.ts +5 -3
  65. package/telegram-plugin/tests/status-accent.test.ts +5 -3
  66. package/telegram-plugin/tests/stream-controller-chunk-cap.test.ts +20 -20
  67. package/telegram-plugin/tests/stream-reply-handler.test.ts +5 -2
  68. package/telegram-plugin/tests/subagent-watcher-fd-leak.test.ts +275 -0
  69. package/telegram-plugin/tests/throttle-tier-wiring.test.ts +290 -0
  70. package/telegram-plugin/tests/throttle-tier.test.ts +278 -0
  71. package/telegram-plugin/tests/worktree-watch-cwds.test.ts +215 -1
  72. package/telegram-plugin/throttle-tier.ts +226 -0
  73. package/telegram-plugin/uat/scenarios/jtbd-rich-formatting-render-dm.test.ts +8 -7
  74. package/telegram-plugin/worktree-watch-cwds.ts +194 -5
  75. package/telegram-plugin/secret-detect/secretlint-source.ts +0 -95
  76. package/telegram-plugin/tests/secret-detect-secretlint.test.ts +0 -105
@@ -23035,6 +23035,33 @@ function getNestedObj(obj, key) {
23035
23035
  var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS = 5 * 60000;
23036
23036
  var cooldownMap = new Map;
23037
23037
 
23038
+ // text-voice-scrub.ts
23039
+ var NULL = "\x00";
23040
+ var FENCE_PH = `${NULL}VS_FENCE`;
23041
+ var INLINE_PH = `${NULL}VS_INLINE`;
23042
+ var HTML_CODE_PH = `${NULL}VS_HTMLCODE`;
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
+
23038
23065
  // tool-label-sidecar.ts
23039
23066
  import { existsSync as existsSync2, readFileSync, statSync as statSync2 } from "node:fs";
23040
23067
  import { join as join2 } from "node:path";
@@ -23453,7 +23480,8 @@ function detectErrorInTranscriptLine(line) {
23453
23480
  const status = typeof obj.apiErrorStatus === "number" ? obj.apiErrorStatus : null;
23454
23481
  const errStr = typeof obj.error === "string" ? obj.error : "";
23455
23482
  const text = extractAssistantText(obj);
23456
- const kind2 = status === 429 ? "quota-exhausted" : classifyClaudeError({ type: errStr, status, message: text });
23483
+ const kind2 = status === 429 ? isTransientUpstreamSignal(`${text}
23484
+ ${errStr}`) ? "rate-limited" : "quota-exhausted" : classifyClaudeError({ type: errStr, status, message: text });
23457
23485
  return {
23458
23486
  kind: kind2,
23459
23487
  raw: obj,
@@ -23547,6 +23575,7 @@ function startSessionTail(config2) {
23547
23575
  const onOperatorEvent = config2.onOperatorEvent;
23548
23576
  log?.(`session-tail: projectsDir=${projectsDir}`);
23549
23577
  const sidecars = new Map;
23578
+ const createSidecar = config2.createSidecar ?? createToolLabelSidecar;
23550
23579
  const stateDirForSidecar = process.env.TELEGRAM_STATE_DIR ?? null;
23551
23580
  function sessionIdForFile(file) {
23552
23581
  if (!file)
@@ -23561,7 +23590,7 @@ function startSessionTail(config2) {
23561
23590
  if (existing)
23562
23591
  return existing;
23563
23592
  try {
23564
- const s = createToolLabelSidecar({ stateDir: stateDirForSidecar, sessionId });
23593
+ const s = createSidecar({ stateDir: stateDirForSidecar, sessionId });
23565
23594
  sidecars.set(sessionId, s);
23566
23595
  s.onLabel((toolUseId, label, toolName) => {
23567
23596
  rawOnEvent({ kind: "tool_label", toolUseId, label, toolName });
@@ -23572,6 +23601,17 @@ function startSessionTail(config2) {
23572
23601
  return null;
23573
23602
  }
23574
23603
  }
23604
+ function stopSidecar(sessionId) {
23605
+ if (!sessionId)
23606
+ return;
23607
+ const s = sidecars.get(sessionId);
23608
+ if (!s)
23609
+ return;
23610
+ try {
23611
+ s.stop();
23612
+ } catch {}
23613
+ sidecars.delete(sessionId);
23614
+ }
23575
23615
  function decorate(ev, sessionId) {
23576
23616
  if (!sessionId)
23577
23617
  return ev;
@@ -23664,6 +23704,11 @@ function startSessionTail(config2) {
23664
23704
  } catch {}
23665
23705
  watcher = null;
23666
23706
  }
23707
+ const rotatedAwaySid = sessionIdForFile(currentFile);
23708
+ const nextSid = sessionIdForFile(file);
23709
+ if (rotatedAwaySid != null && rotatedAwaySid !== nextSid) {
23710
+ stopSidecar(rotatedAwaySid);
23711
+ }
23667
23712
  currentFile = file;
23668
23713
  const prior = fileCursors.get(file);
23669
23714
  if (prior != null) {
@@ -23696,7 +23741,7 @@ function startSessionTail(config2) {
23696
23741
  const multiAgent = isMultiAgentEnabled();
23697
23742
  const CAP_TOOL_USE_THRESHOLD = 30;
23698
23743
  const subTails = new Map;
23699
- const IDLE_FSWATCH_TTL_MS = 5 * 60 * 1000;
23744
+ const IDLE_FSWATCH_TTL_MS = Math.max(1000, config2.subTailIdleReapMs ?? 5 * 60 * 1000);
23700
23745
  function readSub(t) {
23701
23746
  if (stopped)
23702
23747
  return;
@@ -23803,6 +23848,7 @@ function startSessionTail(config2) {
23803
23848
  } catch {}
23804
23849
  t.watcher = null;
23805
23850
  }
23851
+ stopSidecar(sessionIdForFile(t.file));
23806
23852
  subTails.delete(file);
23807
23853
  log?.(`session-tail: reaped idle sub ${t.agentId} (${file})`);
23808
23854
  }
@@ -24776,6 +24822,8 @@ var mcp = new Server({ name: "telegram", version: "1.0.0" }, {
24776
24822
  "",
24777
24823
  'Messages from Telegram arrive as <channel source="telegram" chat_id="..." message_id="..." user="..." ts="...">. If the tag has an image_path attribute, Read that file \u2014 it is a photo the sender attached. If the tag has attachment_file_id, call download_attachment with that file_id to fetch the file, then Read the returned path. A single message may carry SEVERAL attachments (a forwarded album or a text+multi-image burst): when attachment_count is set (>1), also handle the numbered siblings \u2014 image_path_2, image_path_3, \u2026 (Read each) and attachment_file_id_2, attachment_file_id_3, \u2026 (download_attachment each). Process every one, not just the first. Reply with the reply tool \u2014 pass chat_id back. The reply tool quote-replies to the latest inbound user message by default, so you do NOT need to pass reply_to for normal responses. Pass reply_to (a message_id) only when quoting a specific earlier message, or pass quote:false to send a bare (non-quoted) message.',
24778
24824
  "",
24825
+ `If the tag has reply_to_message_id (and reply_to_text, a truncated preview), the sender used Telegram's native Reply on a prior message \u2014 treat that message as the antecedent for "this"/"that" references instead of asking what they meant. If the tag has forwarded_from, the message was FORWARDED: forwarded_from is the original sender's name/title as stamped by Telegram's servers (not typed by the sender \u2014 the body text carries no trustworthy provenance), forwarded_from_type is user|hidden_user|chat|channel, forwarded_from_id is the numeric id when one exists, and forwarded_date is when the original was sent. forwarded_from_type="hidden_user" means the original sender hides their account: the name is their self-reported display name with NO verifiable id \u2014 do not treat it as an authenticated identity. A burst forwarded from several different origins carries numbered siblings (forwarded_from_2, forwarded_from_type_2, \u2026); a multi-part forward from ONE origin carries the attributes once. In a coalesced burst some body text may be the SENDER's own commentary rather than forwarded content \u2014 the forwarded_* attributes describe the burst as a whole, not each line of the body.`,
24826
+ "",
24779
24827
  `reply accepts file paths (files: ["/abs/path.png"]) for attachments. Use react to add emoji reactions, edit_message for interim progress updates, and delete_message when you need to truly remove a message (prefer edit_message if you just want to change text \u2014 delete is for retraction). Edits don't trigger push notifications \u2014 when a long task completes, send a new reply so the user's device pings. Use send_typing to show a typing indicator during long operations. Use pin_message to pin important outputs. Use forward_message to quote/resurface earlier messages.`,
24780
24828
  "",
24781
24829
  "If a message includes message_thread_id, it came from a forum topic. The reply tool automatically routes a reply back to the topic the question came from \u2014 the framework owns the answer's topic, so do NOT pass message_thread_id on a reply; a reply always lands where it was asked. Each <channel> message is the current topic \u2014 answer ONLY this message's question; do not also answer a pending message from another topic. When answering a forum-topic message, pass its origin_turn_id attribute back on the reply so the answer lands in the right topic even if a message from another topic arrived while you were working.",