@integrity-labs/agt-cli 0.28.1014 → 0.28.1016

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.
@@ -30672,6 +30672,26 @@ function parsePeerAgentModeEnv(raw) {
30672
30672
  }
30673
30673
 
30674
30674
  // ../core/dist/channels/governance/sender-policy-decline.js
30675
+ function teamOnlyReason(allowlist) {
30676
+ return allowlist && allowlist.length > 0 ? "mode_team_only" : "mode_team_only_no_principals";
30677
+ }
30678
+ function managerOnlyReason(principalId) {
30679
+ return principalId ? "mode_manager_only" : "mode_manager_only_no_principals";
30680
+ }
30681
+ function classifyTelegramPolicyBlock(policy) {
30682
+ switch (policy.mode) {
30683
+ case "manager_only":
30684
+ return managerOnlyReason(policy.principalTelegramUserId);
30685
+ case "team_only":
30686
+ return teamOnlyReason(policy.teamPrincipalTelegramUserIds);
30687
+ case "team_agents_only":
30688
+ return "mode_team_agents_only";
30689
+ case "agents_only":
30690
+ return "mode_agents_only";
30691
+ case "all":
30692
+ throw new Error("classifyTelegramPolicyBlock called with mode 'all', which never blocks. The caller must have a forward:false verdict from decideSenderPolicyForwardTelegram before classifying.");
30693
+ }
30694
+ }
30675
30695
  function politeDeclineCopy(reason) {
30676
30696
  switch (reason) {
30677
30697
  case "internal_only":
@@ -30690,6 +30710,16 @@ function politeDeclineCopy(reason) {
30690
30710
  return "Sorry, I only respond to other agents in this channel.";
30691
30711
  }
30692
30712
  }
30713
+ function isTelegramDeclineAddressed(input) {
30714
+ if (input.isFromBot)
30715
+ return false;
30716
+ if (input.chatType === "private")
30717
+ return true;
30718
+ if (input.botId !== null && input.replyToFromId !== null && input.replyToFromId !== void 0 && input.replyToFromId === input.botId) {
30719
+ return true;
30720
+ }
30721
+ return !!input.botUsername && typeof input.text === "string" && input.text.toLowerCase().includes(`@${input.botUsername.toLowerCase()}`);
30722
+ }
30693
30723
  function decideDeclineReply(input) {
30694
30724
  const last = input.cache.get(input.key);
30695
30725
  if (last !== void 0) {
@@ -30744,6 +30774,65 @@ function decideInboundAccess(input) {
30744
30774
  return { kind: "admit" };
30745
30775
  }
30746
30776
 
30777
+ // ../core/dist/channels/governance/telegram-inbound-filter.js
30778
+ function isBotSender(evt) {
30779
+ return evt.from?.is_bot === true;
30780
+ }
30781
+ function isSameTeamPeer(evt) {
30782
+ const gate = evt.peerGatePath;
30783
+ return gate === "same_team" || gate === void 0;
30784
+ }
30785
+ function decideSenderPolicyForwardTelegram(evt, policy) {
30786
+ if (policy.mode === "all")
30787
+ return { forward: true };
30788
+ const fromId = evt.from?.id;
30789
+ if (fromId === void 0)
30790
+ return { forward: false, reason: "sender_policy" };
30791
+ const fromIdStr = String(fromId);
30792
+ if (policy.mode === "manager_only") {
30793
+ return policy.principalTelegramUserId && fromIdStr === policy.principalTelegramUserId ? { forward: true } : { forward: false, reason: "sender_policy" };
30794
+ }
30795
+ if (policy.mode === "team_only") {
30796
+ const list = policy.teamPrincipalTelegramUserIds;
30797
+ if (list && list.length > 0 && list.includes(fromIdStr)) {
30798
+ return { forward: true };
30799
+ }
30800
+ return isBotSender(evt) && isSameTeamPeer(evt) ? { forward: true } : { forward: false, reason: "sender_policy" };
30801
+ }
30802
+ if (policy.mode === "team_agents_only") {
30803
+ return isBotSender(evt) && isSameTeamPeer(evt) ? { forward: true } : { forward: false, reason: "sender_policy" };
30804
+ }
30805
+ return isBotSender(evt) ? { forward: true } : { forward: false, reason: "sender_policy" };
30806
+ }
30807
+
30808
+ // ../core/dist/channels/governance/telegram-inbound-config.js
30809
+ function buildTelegramSenderPolicyFromEnv(env2, ctx) {
30810
+ const raw = (env2["TELEGRAM_SENDER_POLICY"] ?? "all").trim().toLowerCase();
30811
+ const teamId = ctx.agentTeamId;
30812
+ if (raw === "all")
30813
+ return { mode: "all" };
30814
+ if (raw === "agents_only")
30815
+ return { mode: "agents_only" };
30816
+ if (raw === "team_agents_only") {
30817
+ if (!teamId)
30818
+ throw new Error("TELEGRAM_SENDER_POLICY=team_agents_only requires AGT_TEAM_ID");
30819
+ return { mode: "team_agents_only", teamId };
30820
+ }
30821
+ if (raw === "team_only") {
30822
+ if (!teamId)
30823
+ throw new Error("TELEGRAM_SENDER_POLICY=team_only requires AGT_TEAM_ID");
30824
+ const teamPrincipalTelegramUserIds = (env2["TELEGRAM_SENDER_POLICY_TEAM_PRINCIPAL_IDS"] ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
30825
+ return { mode: "team_only", teamId, teamPrincipalTelegramUserIds };
30826
+ }
30827
+ if (raw === "manager_only") {
30828
+ if (!teamId)
30829
+ throw new Error("TELEGRAM_SENDER_POLICY=manager_only requires AGT_TEAM_ID");
30830
+ const principalTelegramUserId = env2["TELEGRAM_SENDER_POLICY_PRINCIPAL_ID"];
30831
+ return principalTelegramUserId ? { mode: "manager_only", teamId, principalTelegramUserId } : { mode: "manager_only", teamId };
30832
+ }
30833
+ throw new Error(`Invalid TELEGRAM_SENDER_POLICY=${JSON.stringify(env2["TELEGRAM_SENDER_POLICY"])}`);
30834
+ }
30835
+
30747
30836
  // src/help-command-syntax.ts
30748
30837
  var TELEGRAM_HELP_SYNTAX_RE = /^\/help(?:@([A-Za-z0-9_]{1,64}))?(?:\s|$)/;
30749
30838
  function isTelegramHelpSyntax(text) {
@@ -42868,6 +42957,12 @@ var ALLOWED_CHATS = new Set(
42868
42957
  (process.env.TELEGRAM_ALLOWED_CHATS ?? "").split(",").map((s) => s.trim()).filter(Boolean)
42869
42958
  );
42870
42959
  var PRIVATE_CHATS_ONLY = process.env.TELEGRAM_PRIVATE_CHATS_ONLY === "1";
42960
+ var TELEGRAM_SENDER_POLICY = buildTelegramSenderPolicyFromEnv(
42961
+ process.env,
42962
+ { agentTeamId: process.env.AGT_TEAM_ID ?? null }
42963
+ );
42964
+ var TELEGRAM_DECLINE_CACHE = /* @__PURE__ */ new Map();
42965
+ var TELEGRAM_DECLINE_COOLDOWN_MS = 30 * 60 * 1e3;
42871
42966
  var DIAGNOSTIC_CHAT_IDS = new Set(
42872
42967
  (process.env.TELEGRAM_DIAGNOSTIC_CHAT_IDS ?? "").split(",").map((s) => s.trim()).filter(Boolean)
42873
42968
  );
@@ -46523,6 +46618,19 @@ async function pollLoop() {
46523
46618
  };
46524
46619
  }
46525
46620
  }
46621
+ const telegramPeerGatePath = isFromBot && msg.from ? PEER_CLASSIFIER_CONFIG.peers.find((pr) => pr.bot_id === msg.from.id)?.gate_path : void 0;
46622
+ const telegramSenderPolicyVerdict = decideSenderPolicyForwardTelegram(
46623
+ {
46624
+ from: msg.from ? { id: msg.from.id, username: msg.from.username, is_bot: msg.from.is_bot } : void 0,
46625
+ chat: { id: msg.chat?.id ?? 0, type: msg.chat?.type ?? "" },
46626
+ peerGatePath: telegramPeerGatePath
46627
+ },
46628
+ TELEGRAM_SENDER_POLICY
46629
+ );
46630
+ const telegramSenderPolicyLeaf = telegramSenderPolicyVerdict.forward ? { forward: true } : {
46631
+ forward: false,
46632
+ reason: classifyTelegramPolicyBlock(TELEGRAM_SENDER_POLICY)
46633
+ };
46526
46634
  const access = decideInboundAccess({
46527
46635
  content: { forward: true },
46528
46636
  // empty content already filtered above
@@ -46542,14 +46650,97 @@ async function pollLoop() {
46542
46650
  // agent is not permitted to be in.
46543
46651
  chatType: PRIVATE_CHATS_ONLY && !isTelegramPrivateChat(msg.chat?.type) ? { forward: false } : { forward: true },
46544
46652
  command: telegramCommand ? { command: telegramCommand, authorized: true } : void 0,
46545
- // No sender-policy mode system on Telegram, so no same-org decline.
46546
- sameOrg: false
46653
+ // ENG-9665: the sender-policy axis. Applied to ALL senders - a
46654
+ // label-bearing peer agent is gated here too, before peer governance
46655
+ // - matching Slack and Teams. `mode: 'all'` is a no-op, so an agent
46656
+ // with no policy set behaves exactly as before.
46657
+ senderPolicy: telegramSenderPolicyLeaf,
46658
+ // `sameOrg` decides decline-vs-silence. Telegram puts no membership
46659
+ // signal on an inbound, only the chat id, so a sender counts as
46660
+ // same-org ONLY when the operator explicitly listed this chat in
46661
+ // TELEGRAM_ALLOWED_CHATS. Conservative in the existence-leak
46662
+ // direction on purpose: an agent with no allowlist stays silent
46663
+ // rather than confirming it exists to a stranger.
46664
+ //
46665
+ // WHAT THAT COSTS, recorded because the paragraph above only stated
46666
+ // the benefit (Obi Wan `70c8a2bc8bca`). The chat allowlist is
46667
+ // OPTIONAL and usually unset, so `sameOrg` is false for most agents,
46668
+ // so the whole ENG-10440 decline path is unreachable for them. A real
46669
+ // team member whose `telegram_chat_id` was never linked is then
46670
+ // dropped by `team_only` in COMPLETE SILENCE - no decline to her, and
46671
+ // no alert, because `planSenderPolicyCoverageAlerts` opens only at
46672
+ // `deliverable === 0` and partial coverage is not zero. That is the
46673
+ // shape of the two-month outage ENG-10440 was filed for, and Slack is
46674
+ // strictly better off here because `isSameSlackWorkspace`'s auth.test
46675
+ // fallback keeps its decline reachable.
46676
+ //
46677
+ // NOT fixed by guessing: at this point a team member with no linked
46678
+ // id is byte-for-byte indistinguishable from a stranger, so any
46679
+ // heuristic that declined to her would also decline to him, which is
46680
+ // the existence leak this field exists to prevent. The tractable half
46681
+ // is the ALERT - comparing `deliverable` against `coverage.
46682
+ // resolvedPeople` would catch the partial case - and that is a
46683
+ // separate change: it needs its own alert type (the current one is
46684
+ // literally named `sender_policy_zero_identities`), its own
46685
+ // threshold and its own noise budget across the fleet.
46686
+ sameOrg: ALLOWED_CHATS.size > 0 && ALLOWED_CHATS.has(chatId)
46547
46687
  });
46548
- if (access.kind === "drop" && (access.reason === "org_boundary" || access.reason === "chat_type" || access.reason.startsWith("content:"))) {
46688
+ if (access.kind === "drop" && (access.reason === "org_boundary" || access.reason === "chat_type" || access.reason.startsWith("sender_policy:") || access.reason.startsWith("content:"))) {
46549
46689
  process.stderr.write(
46550
46690
  `telegram-channel(${AGENT_CODE_NAME}): inbound drop reason=${access.reason} chat=${redactId(chatId)}
46551
46691
  `
46552
46692
  );
46693
+ const selfIdentity = access.decline !== void 0 ? await resolveBotIdentity() : null;
46694
+ if (access.decline !== void 0 && msg.from?.id != null && selfIdentity !== null && isTelegramDeclineAddressed({
46695
+ chatType: msg.chat?.type,
46696
+ text: msg.text,
46697
+ botUsername: selfIdentity.bot_username,
46698
+ botId: selfIdentity.bot_id,
46699
+ replyToFromId: msg.reply_to_message?.from?.id ?? null,
46700
+ isFromBot: msg.from?.is_bot === true
46701
+ })) {
46702
+ const subReason = classifyTelegramPolicyBlock(TELEGRAM_SENDER_POLICY);
46703
+ const gate = decideDeclineReply({
46704
+ cache: TELEGRAM_DECLINE_CACHE,
46705
+ key: `${chatId}|${msg.from.id}|${subReason}`,
46706
+ cooldownMs: TELEGRAM_DECLINE_COOLDOWN_MS,
46707
+ now: Date.now()
46708
+ });
46709
+ if (gate.reply) {
46710
+ const declineKey = `${chatId}|${msg.from.id}|${subReason}`;
46711
+ const resp2 = await telegramApiCall(
46712
+ "sendMessage",
46713
+ {
46714
+ chat_id: chatId,
46715
+ text: politeDeclineCopy(subReason),
46716
+ reply_to_message_id: msg.message_id
46717
+ },
46718
+ 1e4
46719
+ ).catch((err) => {
46720
+ TELEGRAM_DECLINE_CACHE.delete(declineKey);
46721
+ process.stderr.write(
46722
+ // Obi Wan `7ac26ddc2aea`: redact the path shapes, as handleHelpCommand does.
46723
+ `telegram-channel(${AGENT_CODE_NAME}): decline reply failed: ${redactAugmentedPaths(err.message)}
46724
+ `
46725
+ );
46726
+ return null;
46727
+ });
46728
+ if (resp2 !== null && resp2.ok !== true) {
46729
+ TELEGRAM_DECLINE_CACHE.delete(declineKey);
46730
+ process.stderr.write(
46731
+ // Obi Wan `7ac26ddc2aea`: `redactId`, not the raw id. Every other
46732
+ // `chat=` log in this file redacts, including the drop line just
46733
+ // above - and in a PRIVATE chat a Telegram chat id IS the user's
46734
+ // telegram user id, the exact `contact_preferences.telegram_chat_id`
46735
+ // value ENG-9662 spent a ticket keeping off the wire. Logging it raw
46736
+ // in a change whose whole premise is the privacy of those ids was the
46737
+ // worst possible place to break the convention.
46738
+ `telegram-channel(${AGENT_CODE_NAME}): decline reply rejected chat=${redactId(chatId)} reason=${subReason}: ${String(resp2.description ?? "no description")}
46739
+ `
46740
+ );
46741
+ }
46742
+ }
46743
+ }
46553
46744
  continue;
46554
46745
  }
46555
46746
  const perfReplyRef = typeof msg.text === "string" && msg.text.trim().length > 0 && msg.reply_to_message?.from?.is_bot === true && msg.reply_to_message?.text ? PERF_REVIEW_REF_RE.exec(msg.reply_to_message.text) : null;
@@ -65,8 +65,8 @@ import {
65
65
  writeDirectChatSessionState,
66
66
  writeEgressAllowlist,
67
67
  writePersistentClaudeWrapper
68
- } from "./chunk-QBQLG2PY.js";
69
- import "./chunk-MLCGPEN6.js";
68
+ } from "./chunk-YPS4AUHN.js";
69
+ import "./chunk-B7XGAC54.js";
70
70
  import "./chunk-DHWNVVX4.js";
71
71
  import "./chunk-XWVM4KPK.js";
72
72
  import "./chunk-EL5MWGGH.js";
@@ -139,4 +139,4 @@ export {
139
139
  writeEgressAllowlist,
140
140
  writePersistentClaudeWrapper
141
141
  };
142
- //# sourceMappingURL=persistent-session-IEGA6OSO.js.map
142
+ //# sourceMappingURL=persistent-session-AQGEDGJR.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-QBQLG2PY.js";
4
- import "./chunk-MLCGPEN6.js";
3
+ } from "./chunk-YPS4AUHN.js";
4
+ import "./chunk-B7XGAC54.js";
5
5
  import "./chunk-DHWNVVX4.js";
6
6
  import "./chunk-XWVM4KPK.js";
7
7
  import "./chunk-EL5MWGGH.js";
@@ -802,4 +802,4 @@ export {
802
802
  readAndResetSlackReplyBindingClassifications,
803
803
  readAndResetSlackReplyTargetClassifications
804
804
  };
805
- //# sourceMappingURL=responsiveness-probe-KWFVG5EI.js.map
805
+ //# sourceMappingURL=responsiveness-probe-BDARO76E.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-MLCGPEN6.js";
3
+ } from "./chunk-B7XGAC54.js";
4
4
  import "./chunk-DGUM43GV.js";
5
5
 
6
6
  // src/lib/session-auth-dead.ts
@@ -204,4 +204,4 @@ export {
204
204
  decideSessionAuthState,
205
205
  probeSessionAuth
206
206
  };
207
- //# sourceMappingURL=session-auth-dead-P6KOHDYJ.js.map
207
+ //# sourceMappingURL=session-auth-dead-2CCORXIW.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.1014",
3
+ "version": "0.28.1016",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {