switchroom 0.14.29 → 0.14.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.
@@ -49420,8 +49420,8 @@ var {
49420
49420
  } = import__.default;
49421
49421
 
49422
49422
  // src/build-info.ts
49423
- var VERSION = "0.14.29";
49424
- var COMMIT_SHA = "33f6300c";
49423
+ var VERSION = "0.14.31";
49424
+ var COMMIT_SHA = "1aa03b91";
49425
49425
 
49426
49426
  // src/cli/agent.ts
49427
49427
  init_source();
@@ -51839,6 +51839,15 @@ function buildSettingsHooksBlock(p) {
51839
51839
  }
51840
51840
  ]
51841
51841
  },
51842
+ {
51843
+ hooks: [
51844
+ {
51845
+ type: "command",
51846
+ command: wrap("hook:sentinel-reply-guard-pretool", `node "${join8(DOCKER_HOOKS_PATH, "sentinel-reply-guard-pretool.mjs")}"`),
51847
+ timeout: 5
51848
+ }
51849
+ ]
51850
+ },
51842
51851
  {
51843
51852
  matcher: "^(Agent|Task)$",
51844
51853
  hooks: [
@@ -74020,7 +74029,39 @@ var STRUCTURED_PATTERNS = [
74020
74029
  slugHint: "pem_private_key"
74021
74030
  }
74022
74031
  ];
74023
- var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...STRUCTURED_PATTERNS];
74032
+ var PROVIDER_PATTERNS = [
74033
+ { rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
74034
+ { rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
74035
+ { rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
74036
+ { rule_id: "sendgrid_api_key", regex: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "sendgrid_key" },
74037
+ { rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
74038
+ { rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
74039
+ { rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
74040
+ { rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
74041
+ { rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
74042
+ { rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
74043
+ { rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
74044
+ { rule_id: "doppler_token", regex: /\b(dp\.(?:pt|st|ct|sa|scim|audit)\.[A-Za-z0-9]{40,44})\b/g, captureIndex: 1, slugHint: "doppler_token" },
74045
+ { rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
74046
+ { rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
74047
+ { rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
74048
+ { rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
74049
+ { rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
74050
+ { rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
74051
+ { rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
74052
+ { rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
74053
+ { rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
74054
+ { rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
74055
+ { rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
74056
+ { rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
74057
+ { rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
74058
+ { rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
74059
+ { rule_id: "grafana_service_account", regex: /\b(glsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8})\b/g, captureIndex: 1, slugHint: "grafana_token" },
74060
+ { rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
74061
+ { rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
74062
+ { rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
74063
+ ];
74064
+ var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
74024
74065
 
74025
74066
  // telegram-plugin/secret-detect/entropy.ts
74026
74067
  function shannonEntropy(s) {
@@ -74070,6 +74111,54 @@ function scanKeyValue(text) {
74070
74111
  return hits;
74071
74112
  }
74072
74113
 
74114
+ // telegram-plugin/secret-detect/generic-entropy.ts
74115
+ var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
74116
+ var GENERIC_MIN_DISTINCT = 18;
74117
+ var MAX_GENERIC_HITS = 20;
74118
+ function hasDistinctChars(tok, n) {
74119
+ const seen = new Uint8Array(128);
74120
+ let distinct = 0;
74121
+ for (let i = 0;i < tok.length; i++) {
74122
+ const c = tok.charCodeAt(i);
74123
+ if (seen[c] === 0) {
74124
+ seen[c] = 1;
74125
+ if (++distinct >= n)
74126
+ return true;
74127
+ }
74128
+ }
74129
+ return false;
74130
+ }
74131
+ function hasDigit(tok) {
74132
+ for (let i = 0;i < tok.length; i++) {
74133
+ const c = tok.charCodeAt(i);
74134
+ if (c >= 48 && c <= 57)
74135
+ return true;
74136
+ }
74137
+ return false;
74138
+ }
74139
+ function scanGenericSecrets(text) {
74140
+ const hits = [];
74141
+ CANDIDATE_RE.lastIndex = 0;
74142
+ let m;
74143
+ while ((m = CANDIDATE_RE.exec(text)) !== null) {
74144
+ if (hits.length >= MAX_GENERIC_HITS)
74145
+ break;
74146
+ const tok = m[0];
74147
+ if (!hasDigit(tok))
74148
+ continue;
74149
+ if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
74150
+ continue;
74151
+ hits.push({
74152
+ rule_id: "generic_high_entropy",
74153
+ start: m.index,
74154
+ end: m.index + tok.length,
74155
+ matched_text: tok,
74156
+ confidence: "ambiguous"
74157
+ });
74158
+ }
74159
+ return hits;
74160
+ }
74161
+
74073
74162
  // telegram-plugin/secret-detect/chunker.ts
74074
74163
  var CHUNK_THRESHOLD = 32 * 1024;
74075
74164
  var WINDOW_SIZE = 16 * 1024;
@@ -74184,6 +74273,10 @@ function detectSecrets(text) {
74184
74273
  for (const h of kvHits) {
74185
74274
  raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
74186
74275
  }
74276
+ const genHits = scanGenericSecrets(win.text);
74277
+ for (const h of genHits) {
74278
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
74279
+ }
74187
74280
  }
74188
74281
  const deduped = dedupeRaw(raw);
74189
74282
  const final = dropOverlaps(deduped);
@@ -74222,13 +74315,7 @@ function dedupeRaw(raw) {
74222
74315
  return Array.from(seen.values());
74223
74316
  }
74224
74317
  function dropOverlaps(hits) {
74225
- const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
74226
- const out = [];
74227
- for (const h of sorted) {
74228
- const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
74229
- if (!contained)
74230
- out.push(h);
74231
- }
74318
+ const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
74232
74319
  out.sort((a, b) => a.start - b.start || a.end - b.end);
74233
74320
  return out;
74234
74321
  }
@@ -74239,7 +74326,7 @@ function redact(text) {
74239
74326
  if (!text || text.length === 0)
74240
74327
  return text;
74241
74328
  const urlScrubbed = redactUrls(text);
74242
- const hits = detectSecrets(urlScrubbed);
74329
+ const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
74243
74330
  if (hits.length === 0)
74244
74331
  return urlScrubbed;
74245
74332
  const sorted = [...hits].sort((a, b) => b.start - a.start);
@@ -19908,7 +19908,39 @@ var STRUCTURED_PATTERNS = [
19908
19908
  slugHint: "pem_private_key"
19909
19909
  }
19910
19910
  ];
19911
- var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...STRUCTURED_PATTERNS];
19911
+ var PROVIDER_PATTERNS = [
19912
+ { rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
19913
+ { rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
19914
+ { rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
19915
+ { rule_id: "sendgrid_api_key", regex: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "sendgrid_key" },
19916
+ { rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
19917
+ { rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
19918
+ { rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
19919
+ { rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
19920
+ { rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
19921
+ { rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
19922
+ { rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
19923
+ { rule_id: "doppler_token", regex: /\b(dp\.(?:pt|st|ct|sa|scim|audit)\.[A-Za-z0-9]{40,44})\b/g, captureIndex: 1, slugHint: "doppler_token" },
19924
+ { rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
19925
+ { rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
19926
+ { rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
19927
+ { rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
19928
+ { rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
19929
+ { rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
19930
+ { rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
19931
+ { rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
19932
+ { rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
19933
+ { rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
19934
+ { rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
19935
+ { rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
19936
+ { rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
19937
+ { rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
19938
+ { rule_id: "grafana_service_account", regex: /\b(glsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8})\b/g, captureIndex: 1, slugHint: "grafana_token" },
19939
+ { rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
19940
+ { rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
19941
+ { rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
19942
+ ];
19943
+ var ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
19912
19944
 
19913
19945
  // telegram-plugin/secret-detect/entropy.ts
19914
19946
  function shannonEntropy(s) {
@@ -19958,6 +19990,54 @@ function scanKeyValue(text) {
19958
19990
  return hits;
19959
19991
  }
19960
19992
 
19993
+ // telegram-plugin/secret-detect/generic-entropy.ts
19994
+ var CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
19995
+ var GENERIC_MIN_DISTINCT = 18;
19996
+ var MAX_GENERIC_HITS = 20;
19997
+ function hasDistinctChars(tok, n) {
19998
+ const seen = new Uint8Array(128);
19999
+ let distinct = 0;
20000
+ for (let i = 0;i < tok.length; i++) {
20001
+ const c = tok.charCodeAt(i);
20002
+ if (seen[c] === 0) {
20003
+ seen[c] = 1;
20004
+ if (++distinct >= n)
20005
+ return true;
20006
+ }
20007
+ }
20008
+ return false;
20009
+ }
20010
+ function hasDigit(tok) {
20011
+ for (let i = 0;i < tok.length; i++) {
20012
+ const c = tok.charCodeAt(i);
20013
+ if (c >= 48 && c <= 57)
20014
+ return true;
20015
+ }
20016
+ return false;
20017
+ }
20018
+ function scanGenericSecrets(text) {
20019
+ const hits = [];
20020
+ CANDIDATE_RE.lastIndex = 0;
20021
+ let m;
20022
+ while ((m = CANDIDATE_RE.exec(text)) !== null) {
20023
+ if (hits.length >= MAX_GENERIC_HITS)
20024
+ break;
20025
+ const tok = m[0];
20026
+ if (!hasDigit(tok))
20027
+ continue;
20028
+ if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
20029
+ continue;
20030
+ hits.push({
20031
+ rule_id: "generic_high_entropy",
20032
+ start: m.index,
20033
+ end: m.index + tok.length,
20034
+ matched_text: tok,
20035
+ confidence: "ambiguous"
20036
+ });
20037
+ }
20038
+ return hits;
20039
+ }
20040
+
19961
20041
  // telegram-plugin/secret-detect/chunker.ts
19962
20042
  var CHUNK_THRESHOLD = 32 * 1024;
19963
20043
  var WINDOW_SIZE = 16 * 1024;
@@ -20072,6 +20152,10 @@ function detectSecrets(text) {
20072
20152
  for (const h of kvHits) {
20073
20153
  raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
20074
20154
  }
20155
+ const genHits = scanGenericSecrets(win.text);
20156
+ for (const h of genHits) {
20157
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
20158
+ }
20075
20159
  }
20076
20160
  const deduped = dedupeRaw(raw);
20077
20161
  const final = dropOverlaps(deduped);
@@ -20110,13 +20194,7 @@ function dedupeRaw(raw) {
20110
20194
  return Array.from(seen.values());
20111
20195
  }
20112
20196
  function dropOverlaps(hits) {
20113
- const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
20114
- const out = [];
20115
- for (const h of sorted) {
20116
- const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
20117
- if (!contained)
20118
- out.push(h);
20119
- }
20197
+ const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
20120
20198
  out.sort((a, b) => a.start - b.start || a.end - b.end);
20121
20199
  return out;
20122
20200
  }
@@ -20127,7 +20205,7 @@ function redact(text) {
20127
20205
  if (!text || text.length === 0)
20128
20206
  return text;
20129
20207
  const urlScrubbed = redactUrls(text);
20130
- const hits = detectSecrets(urlScrubbed);
20208
+ const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
20131
20209
  if (hits.length === 0)
20132
20210
  return urlScrubbed;
20133
20211
  const sorted = [...hits].sort((a, b) => b.start - a.start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.14.29",
3
+ "version": "0.14.31",
4
4
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24867,7 +24867,7 @@ var init_loader = __esm(() => {
24867
24867
  });
24868
24868
 
24869
24869
  // secret-detect/patterns.ts
24870
- var ANCHORED_PATTERNS, STRUCTURED_PATTERNS, ALL_PATTERNS;
24870
+ var ANCHORED_PATTERNS, STRUCTURED_PATTERNS, PROVIDER_PATTERNS, ALL_PATTERNS;
24871
24871
  var init_patterns = __esm(() => {
24872
24872
  ANCHORED_PATTERNS = [
24873
24873
  { rule_id: "anthropic_api_key", regex: /\b(sk-ant-[A-Za-z0-9_-]{8,})\b/g, captureIndex: 1, slugHint: "anthropic_api_key" },
@@ -24925,7 +24925,39 @@ var init_patterns = __esm(() => {
24925
24925
  slugHint: "pem_private_key"
24926
24926
  }
24927
24927
  ];
24928
- ALL_PATTERNS = [...ANCHORED_PATTERNS, ...STRUCTURED_PATTERNS];
24928
+ PROVIDER_PATTERNS = [
24929
+ { rule_id: "slack_webhook", regex: /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9_/]+)/g, captureIndex: 1, slugHint: "slack_webhook" },
24930
+ { rule_id: "stripe_live_secret", regex: /\b(sk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
24931
+ { rule_id: "stripe_restricted", regex: /\b(rk_live_[A-Za-z0-9]{24,})\b/g, captureIndex: 1, slugHint: "stripe_key" },
24932
+ { rule_id: "sendgrid_api_key", regex: /\b(SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "sendgrid_key" },
24933
+ { rule_id: "gitlab_pat", regex: /\b(glpat-[A-Za-z0-9_-]{20})\b/g, captureIndex: 1, slugHint: "gitlab_pat" },
24934
+ { rule_id: "huggingface_token", regex: /\b(hf_[A-Za-z0-9]{34,})\b/g, captureIndex: 1, slugHint: "huggingface_token" },
24935
+ { rule_id: "twilio_api_key", regex: /\b(SK[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "twilio_api_key" },
24936
+ { rule_id: "mailgun_key", regex: /\b(key-[0-9a-f]{32})\b/g, captureIndex: 1, slugHint: "mailgun_key" },
24937
+ { rule_id: "digitalocean_pat", regex: /\b(dop_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
24938
+ { rule_id: "digitalocean_oauth", regex: /\b(doo_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
24939
+ { rule_id: "digitalocean_refresh", regex: /\b(dor_v1_[a-f0-9]{64})\b/g, captureIndex: 1, slugHint: "digitalocean_token" },
24940
+ { rule_id: "doppler_token", regex: /\b(dp\.(?:pt|st|ct|sa|scim|audit)\.[A-Za-z0-9]{40,44})\b/g, captureIndex: 1, slugHint: "doppler_token" },
24941
+ { rule_id: "linear_api_key", regex: /\b(lin_api_[A-Za-z0-9]{40})\b/g, captureIndex: 1, slugHint: "linear_api_key" },
24942
+ { rule_id: "shopify_access_token", regex: /\b(shpat_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
24943
+ { rule_id: "shopify_shared_secret", regex: /\b(shpss_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
24944
+ { rule_id: "shopify_private_app", regex: /\b(shppa_[a-fA-F0-9]{32})\b/g, captureIndex: 1, slugHint: "shopify_token" },
24945
+ { rule_id: "square_access_token", regex: /\b(sq0atp-[A-Za-z0-9_-]{22})\b/g, captureIndex: 1, slugHint: "square_token" },
24946
+ { rule_id: "square_oauth_secret", regex: /\b(sq0csp-[A-Za-z0-9_-]{43})\b/g, captureIndex: 1, slugHint: "square_token" },
24947
+ { rule_id: "newrelic_key", regex: /\b(NRAK-[A-Z0-9]{27})\b/g, captureIndex: 1, slugHint: "newrelic_key" },
24948
+ { rule_id: "notion_token", regex: /\b(ntn_[A-Za-z0-9]{46})\b/g, captureIndex: 1, slugHint: "notion_token" },
24949
+ { rule_id: "planetscale_password", regex: /\b(pscale_pw_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
24950
+ { rule_id: "planetscale_token", regex: /\b(pscale_tkn_[A-Za-z0-9_.-]{43})\b/g, captureIndex: 1, slugHint: "planetscale_token" },
24951
+ { rule_id: "supabase_service_key", regex: /\b(sbp_[a-f0-9]{40})\b/g, captureIndex: 1, slugHint: "supabase_key" },
24952
+ { rule_id: "atlassian_token", regex: /\b(ATATT[A-Za-z0-9_\-=]{20,})\b/g, captureIndex: 1, slugHint: "atlassian_token" },
24953
+ { rule_id: "dropbox_token", regex: /\b(sl\.[A-Za-z0-9_-]{130,})/g, captureIndex: 1, slugHint: "dropbox_token" },
24954
+ { rule_id: "databricks_token", regex: /\b(dapi[a-f0-9]{32})\b/g, captureIndex: 1, slugHint: "databricks_token" },
24955
+ { rule_id: "grafana_service_account", regex: /\b(glsa_[A-Za-z0-9]{32}_[a-fA-F0-9]{8})\b/g, captureIndex: 1, slugHint: "grafana_token" },
24956
+ { rule_id: "pypi_token", regex: /\b(pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,})/g, captureIndex: 1, slugHint: "pypi_token" },
24957
+ { rule_id: "aws_temp_access_key", regex: /\b(ASIA[0-9A-Z]{16})\b/g, captureIndex: 1, slugHint: "aws_access_key" },
24958
+ { rule_id: "gcp_oauth_token", regex: /\b(ya29\.[A-Za-z0-9_-]{30,})/g, captureIndex: 1, slugHint: "gcp_oauth_token" }
24959
+ ];
24960
+ ALL_PATTERNS = [...ANCHORED_PATTERNS, ...PROVIDER_PATTERNS, ...STRUCTURED_PATTERNS];
24929
24961
  });
24930
24962
 
24931
24963
  // secret-detect/entropy.ts
@@ -24978,6 +25010,55 @@ var init_kv_scanner = __esm(() => {
24978
25010
  KV_RE = /\b([A-Za-z_][A-Za-z0-9_-]*(?:password|passwd|token|secret|key|api[_-]?key))\s*[:=]\s*["']?([^\s"'\\]{8,})["']?/gi;
24979
25011
  });
24980
25012
 
25013
+ // secret-detect/generic-entropy.ts
25014
+ function hasDistinctChars(tok, n) {
25015
+ const seen = new Uint8Array(128);
25016
+ let distinct = 0;
25017
+ for (let i = 0;i < tok.length; i++) {
25018
+ const c = tok.charCodeAt(i);
25019
+ if (seen[c] === 0) {
25020
+ seen[c] = 1;
25021
+ if (++distinct >= n)
25022
+ return true;
25023
+ }
25024
+ }
25025
+ return false;
25026
+ }
25027
+ function hasDigit(tok) {
25028
+ for (let i = 0;i < tok.length; i++) {
25029
+ const c = tok.charCodeAt(i);
25030
+ if (c >= 48 && c <= 57)
25031
+ return true;
25032
+ }
25033
+ return false;
25034
+ }
25035
+ function scanGenericSecrets(text) {
25036
+ const hits = [];
25037
+ CANDIDATE_RE.lastIndex = 0;
25038
+ let m;
25039
+ while ((m = CANDIDATE_RE.exec(text)) !== null) {
25040
+ if (hits.length >= MAX_GENERIC_HITS)
25041
+ break;
25042
+ const tok = m[0];
25043
+ if (!hasDigit(tok))
25044
+ continue;
25045
+ if (!hasDistinctChars(tok, GENERIC_MIN_DISTINCT))
25046
+ continue;
25047
+ hits.push({
25048
+ rule_id: "generic_high_entropy",
25049
+ start: m.index,
25050
+ end: m.index + tok.length,
25051
+ matched_text: tok,
25052
+ confidence: "ambiguous"
25053
+ });
25054
+ }
25055
+ return hits;
25056
+ }
25057
+ var CANDIDATE_RE, GENERIC_MIN_DISTINCT = 18, MAX_GENERIC_HITS = 20;
25058
+ var init_generic_entropy = __esm(() => {
25059
+ CANDIDATE_RE = /[A-Za-z0-9]{28,}/g;
25060
+ });
25061
+
24981
25062
  // secret-detect/chunker.ts
24982
25063
  function chunk(text) {
24983
25064
  if (text.length <= CHUNK_THRESHOLD) {
@@ -27070,6 +27151,10 @@ function detectSecrets(text) {
27070
27151
  for (const h of kvHits) {
27071
27152
  raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
27072
27153
  }
27154
+ const genHits = scanGenericSecrets(win.text);
27155
+ for (const h of genHits) {
27156
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
27157
+ }
27073
27158
  }
27074
27159
  const deduped = dedupeRaw(raw);
27075
27160
  const final = dropOverlaps(deduped);
@@ -27108,19 +27193,14 @@ function dedupeRaw(raw) {
27108
27193
  return Array.from(seen.values());
27109
27194
  }
27110
27195
  function dropOverlaps(hits) {
27111
- const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
27112
- const out = [];
27113
- for (const h of sorted) {
27114
- const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
27115
- if (!contained)
27116
- out.push(h);
27117
- }
27196
+ const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
27118
27197
  out.sort((a, b) => a.start - b.start || a.end - b.end);
27119
27198
  return out;
27120
27199
  }
27121
27200
  var init_secret_detect = __esm(() => {
27122
27201
  init_patterns();
27123
27202
  init_kv_scanner();
27203
+ init_generic_entropy();
27124
27204
  init_chunker();
27125
27205
  init_suppressor();
27126
27206
  init_url_redact();
@@ -27132,7 +27212,7 @@ function redact(text) {
27132
27212
  if (!text || text.length === 0)
27133
27213
  return text;
27134
27214
  const urlScrubbed = redactUrls(text);
27135
- const hits = detectSecrets(urlScrubbed);
27215
+ const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
27136
27216
  if (hits.length === 0)
27137
27217
  return urlScrubbed;
27138
27218
  const sorted = [...hits].sort((a, b) => b.start - a.start);
@@ -30521,6 +30601,7 @@ var init_materialize_bot_token = __esm(() => {
30521
30601
  var exports_tmux = {};
30522
30602
  __export(exports_tmux, {
30523
30603
  sendAgentInterrupt: () => sendAgentInterrupt,
30604
+ clearAgentComposer: () => clearAgentComposer,
30524
30605
  captureAgentPane: () => captureAgentPane
30525
30606
  });
30526
30607
  import { execFileSync as execFileSync4 } from "node:child_process";
@@ -30616,6 +30697,22 @@ function sendAgentInterrupt(opts) {
30616
30697
  }
30617
30698
  return { error: lastError ?? "tmux send-keys C-c failed" };
30618
30699
  }
30700
+ function clearAgentComposer(opts) {
30701
+ const { agentName: agentName3 } = opts;
30702
+ const socket = `switchroom-${agentName3}`;
30703
+ const args = ["-L", socket, "send-keys", "-t", agentName3, "C-u", "C-a", "C-k"];
30704
+ try {
30705
+ execFileSync4("tmux", args, {
30706
+ timeout: 3000,
30707
+ stdio: ["ignore", "pipe", "pipe"]
30708
+ });
30709
+ return { ok: true };
30710
+ } catch (err) {
30711
+ const msg = `tmux send-keys composer-clear failed: ${err.message}`;
30712
+ console.error(`[tmux-composer-clear] ${agentName3}: ${msg}`);
30713
+ return { error: msg };
30714
+ }
30715
+ }
30619
30716
  function sleepSync2(ms) {
30620
30717
  const sab = new SharedArrayBuffer(4);
30621
30718
  const view = new Int32Array(sab);
@@ -38932,7 +39029,7 @@ var EDIT_INTERVAL_MS = 60000;
38932
39029
  var POLL_INTERVAL_MS = 5000;
38933
39030
  var MAX_LIFETIME_MS = 30 * 60000;
38934
39031
  var TELEGRAM_MSG_CAP2 = 4000;
38935
- var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)$/;
39032
+ var SUFFIX_RE = /\n\n\u2014 still working \(\d+m\)( \u00b7 message me anytime, I'll keep you posted)?$/;
38936
39033
  var stateByKey = new Map;
38937
39034
  var timer2 = null;
38938
39035
  var activeDeps2 = null;
@@ -39044,7 +39141,7 @@ function tick2(now) {
39044
39141
  const minutes = Math.max(1, Math.round(elapsed / 60000));
39045
39142
  const suffix = `
39046
39143
 
39047
- \u2014 still working (${minutes}m)`;
39144
+ \u2014 still working (${minutes}m) \u00b7 message me anytime, I'll keep you posted`;
39048
39145
  const newText = s.anchorOriginalText + suffix;
39049
39146
  if (newText.length > TELEGRAM_MSG_CAP2) {
39050
39147
  s.lastEditAt = now;
@@ -42495,6 +42592,15 @@ function isCompositeSilentNoise(text) {
42495
42592
  return false;
42496
42593
  return lines.every((l) => isSilentFlushMarker2(l) || isTrivialConfirmationLine(l));
42497
42594
  }
42595
+ function endsWithSilentMarker(text) {
42596
+ if (typeof text !== "string")
42597
+ return false;
42598
+ const lines = text.split(`
42599
+ `).map((l) => l.trim()).filter((l) => l.length > 0);
42600
+ if (lines.length === 0)
42601
+ return false;
42602
+ return isSilentFlushMarker2(lines[lines.length - 1]);
42603
+ }
42498
42604
  function decideTurnFlush(input) {
42499
42605
  const flushEnabled = input.flushEnabled !== false;
42500
42606
  if (!flushEnabled)
@@ -42511,6 +42617,8 @@ function decideTurnFlush(input) {
42511
42617
  return { kind: "skip", reason: "silent-marker" };
42512
42618
  if (isCompositeSilentNoise(joined))
42513
42619
  return { kind: "skip", reason: "silent-marker" };
42620
+ if (endsWithSilentMarker(joined))
42621
+ return { kind: "skip", reason: "silent-marker" };
42514
42622
  return { kind: "flush", text: joined };
42515
42623
  }
42516
42624
  function isTurnFlushSafetyEnabled(env = process.env) {
@@ -48810,6 +48918,7 @@ function recentDenialsFromAuditLog(rawAuditLog, opts) {
48810
48918
  // secret-detect/index.ts
48811
48919
  init_patterns();
48812
48920
  init_kv_scanner();
48921
+ init_generic_entropy();
48813
48922
  init_chunker();
48814
48923
  init_suppressor();
48815
48924
  init_url_redact();
@@ -48859,6 +48968,10 @@ function detectSecrets2(text) {
48859
48968
  for (const h of kvHits) {
48860
48969
  raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
48861
48970
  }
48971
+ const genHits = scanGenericSecrets(win.text);
48972
+ for (const h of genHits) {
48973
+ raw.push({ ...h, start: h.start + win.offset, end: h.end + win.offset });
48974
+ }
48862
48975
  }
48863
48976
  const deduped = dedupeRaw2(raw);
48864
48977
  const final = dropOverlaps2(deduped);
@@ -48897,13 +49010,7 @@ function dedupeRaw2(raw) {
48897
49010
  return Array.from(seen.values());
48898
49011
  }
48899
49012
  function dropOverlaps2(hits) {
48900
- const sorted = [...hits].sort((a, b) => a.end - a.start - (b.end - b.start));
48901
- const out = [];
48902
- for (const h of sorted) {
48903
- const contained = out.some((existing) => existing !== h && existing.start <= h.start && existing.end >= h.end && !(existing.start === h.start && existing.end === h.end));
48904
- if (!contained)
48905
- out.push(h);
48906
- }
49013
+ const out = hits.filter((h) => !(h.confidence === "ambiguous" && hits.some((o) => o !== h && o.start <= h.start && o.end >= h.end && !(o.start === h.start && o.end === h.end))));
48907
49014
  out.sort((a, b) => a.start - b.start || a.end - b.end);
48908
49015
  return out;
48909
49016
  }
@@ -48916,7 +49023,7 @@ function redact2(text) {
48916
49023
  if (!text || text.length === 0)
48917
49024
  return text;
48918
49025
  const urlScrubbed = redactUrls(text);
48919
- const hits = detectSecrets(urlScrubbed);
49026
+ const hits = detectSecrets(urlScrubbed).filter((h) => h.rule_id !== "generic_high_entropy");
48920
49027
  if (hits.length === 0)
48921
49028
  return urlScrubbed;
48922
49029
  const sorted = [...hits].sort((a, b) => b.start - a.start);
@@ -51659,10 +51766,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
51659
51766
  }
51660
51767
 
51661
51768
  // ../src/build-info.ts
51662
- var VERSION = "0.14.29";
51663
- var COMMIT_SHA = "33f6300c";
51664
- var COMMIT_DATE = "2026-06-01T04:54:34Z";
51665
- var LATEST_PR = 2052;
51769
+ var VERSION = "0.14.31";
51770
+ var COMMIT_SHA = "1aa03b91";
51771
+ var COMMIT_DATE = "2026-06-01T06:45:00Z";
51772
+ var LATEST_PR = 2060;
51666
51773
  var COMMITS_AHEAD_OF_TAG = 0;
51667
51774
 
51668
51775
  // gateway/boot-version.ts
@@ -57717,6 +57824,19 @@ ${preBlock(write.output)}`;
57717
57824
  `);
57718
57825
  return;
57719
57826
  }
57827
+ if (selfAgent) {
57828
+ try {
57829
+ const { clearAgentComposer: clearAgentComposer2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
57830
+ const cleared = clearAgentComposer2({ agentName: selfAgent });
57831
+ if ("error" in cleared) {
57832
+ process.stderr.write(`telegram gateway: pre-send composer-clear soft-failed agent=${selfAgent}: ${cleared.error} \u2014 delivering anyway
57833
+ `);
57834
+ }
57835
+ } catch (err) {
57836
+ process.stderr.write(`telegram gateway: pre-send composer-clear threw agent=${selfAgent}: ${err.message} \u2014 delivering anyway
57837
+ `);
57838
+ }
57839
+ }
57720
57840
  const delivered = ipcServer.sendToAgent(selfAgent, inboundMsg);
57721
57841
  if (delivered)
57722
57842
  markClaudeBusyForInbound(inboundMsg);
@@ -10515,6 +10515,33 @@ async function handleInbound(
10515
10515
  return
10516
10516
  }
10517
10517
 
10518
+ // Pre-send composer clear (the marko wedge). The inbound is about to be
10519
+ // delivered as an MCP `notifications/claude/channel` notification, which
10520
+ // the unmodified CLI appends into its composer and auto-submits ONLY when
10521
+ // the composer is empty + idle. The #1556 gate above guarantees idle, but
10522
+ // NOT empty: stale typed-ahead / ghost text stranded in the composer
10523
+ // (observed live on agent `marko`: "Yes, go ahead on both") makes the
10524
+ // appended inbound fail to submit and silently swallows every subsequent
10525
+ // queued inbound until a hard restart. Wipe the composer first so the
10526
+ // notification lands at a clean line. Soft-fail by contract — a clear
10527
+ // failure (no tmux session under legacy_pty, socket missing, timeout)
10528
+ // must NEVER block delivery; log and proceed.
10529
+ if (selfAgent) {
10530
+ try {
10531
+ const { clearAgentComposer } = await import('../../src/agents/tmux.js')
10532
+ const cleared = clearAgentComposer({ agentName: selfAgent })
10533
+ if ('error' in cleared) {
10534
+ process.stderr.write(
10535
+ `telegram gateway: pre-send composer-clear soft-failed agent=${selfAgent}: ${cleared.error} — delivering anyway\n`,
10536
+ )
10537
+ }
10538
+ } catch (err) {
10539
+ process.stderr.write(
10540
+ `telegram gateway: pre-send composer-clear threw agent=${selfAgent}: ${(err as Error).message} — delivering anyway\n`,
10541
+ )
10542
+ }
10543
+ }
10544
+
10518
10545
  const delivered = ipcServer.sendToAgent(selfAgent, inboundMsg)
10519
10546
  if (delivered) markClaudeBusyForInbound(inboundMsg)
10520
10547
  if (!delivered) {
@@ -10,6 +10,15 @@
10
10
  }
11
11
  ]
12
12
  },
13
+ {
14
+ "hooks": [
15
+ {
16
+ "type": "command",
17
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/sentinel-reply-guard-pretool.mjs\"",
18
+ "timeout": 5
19
+ }
20
+ ]
21
+ },
13
22
  {
14
23
  "matcher": "^(Agent|Task)$",
15
24
  "hooks": [