@integrity-labs/agt-cli 0.28.550 → 0.28.552

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.
@@ -37442,6 +37442,47 @@ var SLACK_EGRESS_TOOLS = /* @__PURE__ */ new Set([
37442
37442
  "slack.update"
37443
37443
  ]);
37444
37444
 
37445
+ // ../core/dist/channels/governance/slack-ingress-only-threads.js
37446
+ function buildIngressThreadKey(channel, threadTs) {
37447
+ if (!channel || !threadTs)
37448
+ return null;
37449
+ return `${channel}:${threadTs}`;
37450
+ }
37451
+ function createIngressOnlyThreadRegistry(maxEntries = 500) {
37452
+ const marked = /* @__PURE__ */ new Set();
37453
+ return {
37454
+ mark(channel, threadTs) {
37455
+ const key2 = buildIngressThreadKey(channel, threadTs);
37456
+ if (!key2)
37457
+ return;
37458
+ marked.delete(key2);
37459
+ marked.add(key2);
37460
+ while (marked.size > maxEntries) {
37461
+ const oldest = marked.values().next();
37462
+ if (oldest.done)
37463
+ break;
37464
+ marked.delete(oldest.value);
37465
+ }
37466
+ },
37467
+ clear(channel, threadTs) {
37468
+ const key2 = buildIngressThreadKey(channel, threadTs);
37469
+ if (!key2)
37470
+ return;
37471
+ marked.delete(key2);
37472
+ },
37473
+ isIngressOnly(channel, threadTs) {
37474
+ const key2 = buildIngressThreadKey(channel, threadTs);
37475
+ if (!key2)
37476
+ return false;
37477
+ return marked.has(key2);
37478
+ },
37479
+ size() {
37480
+ return marked.size;
37481
+ }
37482
+ };
37483
+ }
37484
+ var INGRESS_ONLY_REFUSAL = "This thread was opened by a third-party bot (an alert or issue card), which can wake you but is not someone you can talk to - replying would start a bot-to-bot exchange in a shared channel. Act on the content instead: work the issue, or raise it with a person in a thread they are in. If a human posts here, this thread becomes repliable.";
37485
+
37445
37486
  // src/strip-repost-preamble.ts
37446
37487
  var PRIOR_ATTEMPT_CUE = /\b(a moment ago|just now|a second ago|earlier|moments ago|previously|last (?:time|attempt)|drafted|dialled|dialed|typed|composed|wrote|sent) (?:this|that|it)?\b|\b(?:drafted|dialled|dialed|typed|composed)\b/i;
37447
37488
  var FAILURE_CUE = /\b(?:did(?:n['’]t| not)|could(?:n['’]t| not)|would(?:n['’]t| not)|wasn['’]t|fail(?:ed)?|never)\b[^.!?\n]*?\b(?:parse[d]?|send|sent|go through|went through|land|deliver(?:ed)?|post(?:ed)?|come through|came through)\b/i;
@@ -39956,6 +39997,16 @@ function classifyPeerMessage(msg, cfg, self) {
39956
39997
  }
39957
39998
  const peer = cfg.peers.find((p2) => p2.bot_user_id === msg.user);
39958
39999
  if (!peer) {
40000
+ const thirdParty = cfg.third_party_bots?.find((b) => b.bot_user_id === msg.user);
40001
+ if (thirdParty) {
40002
+ if (cfg.peer_disabled_mode === "cross_team_only") {
40003
+ return { kind: "drop", reason: "peer_disabled_cross_team" };
40004
+ }
40005
+ if (thirdParty.channel_ids.includes(msg.channel)) {
40006
+ return { kind: "bot-ingress", bot: thirdParty };
40007
+ }
40008
+ return { kind: "drop", reason: "third_party_channel_not_allowlisted" };
40009
+ }
39959
40010
  return { kind: "drop", reason: "unknown_peer" };
39960
40011
  }
39961
40012
  if (peer.gate_path === null) {
@@ -40063,6 +40114,33 @@ function parsePeerGroupIdsEnv(raw) {
40063
40114
  return [];
40064
40115
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
40065
40116
  }
40117
+ function parseThirdPartyBotsEnv(raw) {
40118
+ if (!raw || !raw.trim())
40119
+ return [];
40120
+ let parsed;
40121
+ try {
40122
+ parsed = JSON.parse(raw);
40123
+ } catch {
40124
+ return [];
40125
+ }
40126
+ if (!Array.isArray(parsed))
40127
+ return [];
40128
+ const out = [];
40129
+ for (const item of parsed) {
40130
+ if (!item || typeof item !== "object")
40131
+ continue;
40132
+ const rec = item;
40133
+ const botUserId2 = typeof rec["bot_user_id"] === "string" ? rec["bot_user_id"].trim() : "";
40134
+ if (!botUserId2)
40135
+ continue;
40136
+ if (!Array.isArray(rec["channel_ids"]))
40137
+ continue;
40138
+ const channels = rec["channel_ids"].filter((c) => typeof c === "string" && c.trim() !== "").map((c) => c.trim());
40139
+ const label = typeof rec["label"] === "string" && rec["label"].trim() ? rec["label"].trim() : void 0;
40140
+ out.push({ bot_user_id: botUserId2, channel_ids: channels, ...label ? { label } : {} });
40141
+ }
40142
+ return out;
40143
+ }
40066
40144
  function parsePeerAgentModeEnv(raw) {
40067
40145
  if (raw === "listen" || raw === "respond")
40068
40146
  return raw;
@@ -41074,8 +41152,14 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
41074
41152
  peer_agent_mode: parsePeerAgentModeEnv(process.env.SLACK_PEER_AGENT_MODE),
41075
41153
  peer_group_ids: parsePeerGroupIdsEnv(process.env.SLACK_PEER_GROUP_IDS),
41076
41154
  peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
41077
- peer_disabled_mode: SLACK_PEER_DISABLED_MODE
41155
+ peer_disabled_mode: SLACK_PEER_DISABLED_MODE,
41156
+ // ENG-8669: third-party bots (Linear, Amazon Q, CloudWatch, PagerDuty) that
41157
+ // may WAKE this agent, per channel. Absent env = empty = the pre-ENG-8669
41158
+ // behaviour exactly, so this ships inert for every agent that has not opted
41159
+ // in. Ingress-only by construction - see isReplyEligible in the classifier.
41160
+ third_party_bots: parseThirdPartyBotsEnv(process.env.SLACK_THIRD_PARTY_BOTS)
41078
41161
  };
41162
+ var ingressOnlyThreads = createIngressOnlyThreadRegistry();
41079
41163
  var SLACK_TEAM_PEER_USER_IDS = parseTeamPeerUserIdsEnv(
41080
41164
  process.env.SLACK_TEAM_PEER_USER_IDS
41081
41165
  );
@@ -43815,6 +43899,16 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
43815
43899
  if (isImpersonating() && !channelsEnabledOverride() && SLACK_EGRESS_TOOLS.has(name)) {
43816
43900
  return buildImpersonationRefusal(name);
43817
43901
  }
43902
+ if (SLACK_EGRESS_TOOLS.has(name)) {
43903
+ const egressArgs = args ?? {};
43904
+ const targetThread = egressArgs.thread_ts ?? egressArgs.message_ts;
43905
+ if (ingressOnlyThreads.isIngressOnly(egressArgs.channel, targetThread)) {
43906
+ return {
43907
+ content: [{ type: "text", text: INGRESS_ONLY_REFUSAL }],
43908
+ isError: true
43909
+ };
43910
+ }
43911
+ }
43818
43912
  if (name === "slack.reply") {
43819
43913
  const { channel, text, thread_ts, message_ts, interim, inbound_id, proactive, to_channel_root } = args;
43820
43914
  const bindingMode = slackReplyBindingMode();
@@ -45491,6 +45585,12 @@ async function connectSocketMode() {
45491
45585
  });
45492
45586
  const passedIdentity = access.kind !== "drop" || access.reason.startsWith("peer:");
45493
45587
  if (passedIdentity) recordActivity("inbound");
45588
+ const ingressThreadTs = evt.thread_ts ?? evt.ts;
45589
+ if (peerClassification?.kind === "bot-ingress" && access.kind !== "drop") {
45590
+ ingressOnlyThreads.mark(evt.channel, ingressThreadTs);
45591
+ } else if (!isBot) {
45592
+ ingressOnlyThreads.clear(evt.channel, ingressThreadTs);
45593
+ }
45494
45594
  if (access.kind === "drop") {
45495
45595
  const channelHash = createHash3("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
45496
45596
  const subtypeSuffix = access.reason === "content:subtype" ? ` subtype=${evt.subtype ?? "none"}` : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.550",
3
+ "version": "0.28.552",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {