@integrity-labs/agt-cli 0.27.143 → 0.27.145

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.
@@ -14305,6 +14305,45 @@ function readDeclineCooldownMs(envVarName, defaultSeconds = 1800) {
14305
14305
  return parsed * 1e3;
14306
14306
  }
14307
14307
 
14308
+ // src/inbound-access.ts
14309
+ function decideInboundAccess(input) {
14310
+ if (!input.content.forward) {
14311
+ return { kind: "drop", reason: `content:${input.content.reason}` };
14312
+ }
14313
+ if (input.isSelf) {
14314
+ return { kind: "drop", reason: "self" };
14315
+ }
14316
+ if (input.orgBoundary && !input.orgBoundary.forward) {
14317
+ return { kind: "drop", reason: "org_boundary" };
14318
+ }
14319
+ if (input.senderPolicy && !input.senderPolicy.forward) {
14320
+ const reason = input.senderPolicy.reason;
14321
+ const declineWorthy = input.sameOrg && reason !== "internal_only";
14322
+ return {
14323
+ kind: "drop",
14324
+ reason: `sender_policy:${reason}`,
14325
+ decline: declineWorthy ? politeDeclineCopy(reason) : void 0
14326
+ };
14327
+ }
14328
+ if (input.command && !input.isBot) {
14329
+ return {
14330
+ kind: "command",
14331
+ command: input.command.command,
14332
+ authorized: input.command.authorized
14333
+ };
14334
+ }
14335
+ if (input.isBot) {
14336
+ const peer = input.peer;
14337
+ if (!peer || peer.kind === "self") {
14338
+ return { kind: "drop", reason: peer?.kind === "self" ? "self" : "peer:unclassified" };
14339
+ }
14340
+ if (peer.kind === "drop") {
14341
+ return { kind: "drop", reason: `peer:${peer.reason ?? "unspecified"}` };
14342
+ }
14343
+ }
14344
+ return { kind: "admit" };
14345
+ }
14346
+
14308
14347
  // src/ack-reaction.ts
14309
14348
  import { readdirSync, readFileSync } from "fs";
14310
14349
  import { join } from "path";
@@ -15568,6 +15607,19 @@ function mergeInboundText(text, blocks) {
15568
15607
  return { content, recoveredTable: rendered.length > 0 && content !== text };
15569
15608
  }
15570
15609
 
15610
+ // src/slack-allowlist-source.ts
15611
+ function parseAllowedUsersCsv(raw) {
15612
+ return new Set(
15613
+ (raw ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0)
15614
+ );
15615
+ }
15616
+ function extractAllowedUsersFromMcpJson(jsonText) {
15617
+ const parsed = JSON.parse(jsonText);
15618
+ const slackServer = parsed.mcpServers?.["slack"];
15619
+ if (!slackServer) return null;
15620
+ return parseAllowedUsersCsv(slackServer.env?.["SLACK_ALLOWED_USERS"]);
15621
+ }
15622
+
15571
15623
  // src/slack-response-mode.ts
15572
15624
  var MODES = [
15573
15625
  "mention_only",
@@ -16306,9 +16358,7 @@ async function maybeSendSenderPolicyDecline(args) {
16306
16358
  var BLOCK_KIT_ENABLED = process.env.SLACK_BLOCK_KIT_ENABLED === "true";
16307
16359
  var BLOCK_KIT_ASK_USER_ENABLED = process.env.SLACK_BLOCK_KIT_ASK_USER_ENABLED === "true";
16308
16360
  var BLOCK_KIT_DISABLED = process.env.SLACK_BLOCK_KIT_DISABLED === "true";
16309
- var ALLOWED_USERS = new Set(
16310
- (process.env.SLACK_ALLOWED_USERS ?? "").split(",").map((s) => s.trim()).filter(Boolean)
16311
- );
16361
+ var ALLOWED_USERS = parseAllowedUsersCsv(process.env.SLACK_ALLOWED_USERS);
16312
16362
  var THREAD_AUTO_FOLLOW = process.env.SLACK_THREAD_AUTO_FOLLOW ?? "off";
16313
16363
  var CHANNEL_RESPONSE_MODE = parseResponseMode(process.env.SLACK_CHANNEL_RESPONSE_MODE);
16314
16364
  var SLACK_PEER_DISABLED_MODE = (() => {
@@ -16355,6 +16405,28 @@ var SLACK_PEER_CLASSIFIER_CONFIG = {
16355
16405
  peer_disabled_mode: SLACK_PEER_DISABLED_MODE
16356
16406
  };
16357
16407
  var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join6(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
16408
+ var SLACK_MCP_CONFIG_PATH = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "project", ".mcp.json") : null;
16409
+ var liveAllowedUsersCache = null;
16410
+ function readLiveAllowedUsers() {
16411
+ if (!SLACK_MCP_CONFIG_PATH) return null;
16412
+ try {
16413
+ const mtimeMs = statSync2(SLACK_MCP_CONFIG_PATH).mtimeMs;
16414
+ if (liveAllowedUsersCache && liveAllowedUsersCache.mtimeMs === mtimeMs) {
16415
+ return liveAllowedUsersCache.value;
16416
+ }
16417
+ const value = extractAllowedUsersFromMcpJson(
16418
+ readFileSync7(SLACK_MCP_CONFIG_PATH, "utf-8")
16419
+ );
16420
+ if (value === null) return null;
16421
+ liveAllowedUsersCache = { mtimeMs, value };
16422
+ return value;
16423
+ } catch {
16424
+ return null;
16425
+ }
16426
+ }
16427
+ function getEffectiveAllowedUsers() {
16428
+ return readLiveAllowedUsers() ?? ALLOWED_USERS;
16429
+ }
16358
16430
  var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
16359
16431
  var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
16360
16432
  var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join6(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
@@ -17298,7 +17370,7 @@ async function handleDebugSlashCommand(payload, responseUrl) {
17298
17370
  const verdict = evaluateDebugGate({
17299
17371
  channelId: payload.channel_id,
17300
17372
  userId: payload.user_id,
17301
- allowedUsers: ALLOWED_USERS
17373
+ allowedUsers: getEffectiveAllowedUsers()
17302
17374
  });
17303
17375
  const investigateCmd = agentSlashCommand("/investigate");
17304
17376
  if (!verdict.ok) {
@@ -17400,7 +17472,8 @@ async function handleSlashCommandEnvelope(payload) {
17400
17472
  return;
17401
17473
  }
17402
17474
  if (matchesAgentCommand(command, "/restart")) {
17403
- if (ALLOWED_USERS.size > 0 && (!payload.user_id || !ALLOWED_USERS.has(payload.user_id))) {
17475
+ const restartAllowedUsers = getEffectiveAllowedUsers();
17476
+ if (restartAllowedUsers.size > 0 && (!payload.user_id || !restartAllowedUsers.has(payload.user_id))) {
17404
17477
  process.stderr.write(
17405
17478
  `slack-channel(${codeName}): /restart slash-command denied \u2014 user not in SLACK_ALLOWED_USERS
17406
17479
  `
@@ -19035,132 +19108,134 @@ async function connectSocketMode() {
19035
19108
  const evt = msg.payload.event;
19036
19109
  if (evt.user === botUserId) return;
19037
19110
  if (evt.type !== "app_mention" && evt.type !== "message") return;
19111
+ const isBot = Boolean(evt.bot_id);
19038
19112
  const filterDecision = decideSlackMessageForward(evt);
19039
- if (!filterDecision.forward) {
19040
- process.stderr.write(`slack-channel: dropped message event (reason=${filterDecision.reason}, subtype=${evt.subtype ?? "none"}, ts=${evt.ts ?? "n/a"})
19041
- `);
19042
- return;
19043
- }
19044
19113
  const senderPolicyDecision = decideSenderPolicyForward(evt, SLACK_SENDER_POLICY);
19045
- if (!senderPolicyDecision.forward) {
19046
- const subReason = classifySlackPolicyBlock(evt, SLACK_SENDER_POLICY);
19047
- process.stderr.write(`slack-channel: dropped message event (reason=sender_policy, sub=${subReason}, mode=${SLACK_SENDER_POLICY.mode}, ts=${evt.ts ?? "n/a"})
19048
- `);
19049
- await maybeSendSenderPolicyDecline({
19050
- channel: evt.channel,
19051
- senderId: evt.user,
19052
- // CR on PR #1623: top-level posts (DMs, channel root messages)
19053
- // arrive with no `thread_ts` and MUST decline inline, not in a
19054
- // new thread off the inbound message. The `?? evt.ts` fallback
19055
- // forced every root message into a thread reply — exactly the
19056
- // case the helper's "omit thread_ts when undefined" branch was
19057
- // meant to handle. Pass through as-is: in-thread for thread
19058
- // replies (thread_ts present), inline for everything else.
19059
- threadTs: evt.thread_ts,
19060
- subReason
19061
- }).catch((err) => {
19062
- process.stderr.write(
19063
- `slack-channel(${AGENT_CODE_NAME}): decline reply failed: ${err.message}
19064
- `
19065
- );
19066
- });
19067
- return;
19068
- }
19069
- recordActivity("inbound");
19070
- const isDirectMessage = evt.channel?.startsWith("D");
19071
- const isThreadReply = !!evt.thread_ts && evt.thread_ts !== evt.ts;
19072
- const trackTs = evt.thread_ts ?? evt.ts ?? "";
19073
- const threadKey = buildThreadKey(evt.channel, trackTs);
19114
+ const policyBlockReason = senderPolicyDecision.forward ? void 0 : classifySlackPolicyBlock(evt, SLACK_SENDER_POLICY);
19115
+ const peerClassification = isBot ? classifyPeerMessage(
19116
+ {
19117
+ text: evt.text,
19118
+ channel: evt.channel ?? "",
19119
+ channel_type: evt.channel_type,
19120
+ user: evt.user,
19121
+ bot_id: evt.bot_id,
19122
+ thread_ts: evt.thread_ts,
19123
+ parent_user_id: evt.parent_user_id
19124
+ },
19125
+ SLACK_PEER_CLASSIFIER_CONFIG,
19126
+ { bot_user_id: botUserId }
19127
+ ) : void 0;
19074
19128
  const rawText = evt.text ?? "";
19075
19129
  const strippedText = rawText.replace(/^\s*<@[^>]+>\s*/, "").trim();
19076
19130
  const restartSuffixed = agentSlashCommand("/restart");
19131
+ const helpSuffixed = agentSlashCommand("/help");
19077
19132
  const isRestartCommand = strippedText === "/restart" || strippedText.startsWith("/restart ") || strippedText === restartSuffixed || strippedText.startsWith(`${restartSuffixed} `);
19078
- const isHelpCommand = strippedText === "/help" || strippedText.startsWith("/help ");
19079
- if (isHelpCommand) {
19080
- await handleHelpCommand({
19081
- channel: evt.channel ?? "",
19082
- threadTs: evt.thread_ts,
19083
- ts: evt.ts ?? ""
19084
- });
19133
+ const isHelpCommand = strippedText === "/help" || strippedText.startsWith("/help ") || strippedText === helpSuffixed || strippedText.startsWith(`${helpSuffixed} `);
19134
+ const command = isBot ? void 0 : isHelpCommand ? { command: "help", authorized: true } : isRestartCommand ? { command: "restart", authorized: isRestartSenderAllowed(getEffectiveAllowedUsers(), evt.user) } : void 0;
19135
+ const slackHomeTeamId = process.env.SLACK_HOME_TEAM_ID;
19136
+ const sameOrg = !!slackHomeTeamId && evt.team === slackHomeTeamId;
19137
+ const access = decideInboundAccess({
19138
+ content: filterDecision.forward ? { forward: true } : { forward: false, reason: filterDecision.reason },
19139
+ // The agent's own user-echo was already skipped above; peer 'self'
19140
+ // covers the bot-id self case inside the composer.
19141
+ isSelf: false,
19142
+ isBot,
19143
+ peer: peerClassification ? {
19144
+ kind: peerClassification.kind,
19145
+ reason: peerClassification.kind === "drop" ? peerClassification.reason : void 0
19146
+ } : void 0,
19147
+ senderPolicy: senderPolicyDecision.forward ? { forward: true } : { forward: false, reason: policyBlockReason },
19148
+ command,
19149
+ sameOrg
19150
+ });
19151
+ const passedIdentity = access.kind !== "drop" || access.reason.startsWith("peer:");
19152
+ if (passedIdentity) recordActivity("inbound");
19153
+ if (access.kind === "drop") {
19154
+ const channelHash = createHash("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
19155
+ process.stderr.write(
19156
+ `slack-channel: inbound drop reason=${access.reason} channel=${channelHash} ts=${redactSlackId(evt.ts)}
19157
+ `
19158
+ );
19159
+ if (access.decline !== void 0 && policyBlockReason) {
19160
+ await maybeSendSenderPolicyDecline({
19161
+ channel: evt.channel,
19162
+ senderId: evt.user,
19163
+ threadTs: evt.thread_ts,
19164
+ subReason: policyBlockReason
19165
+ }).catch((err) => {
19166
+ process.stderr.write(
19167
+ `slack-channel(${AGENT_CODE_NAME}): decline reply failed: ${err.message}
19168
+ `
19169
+ );
19170
+ });
19171
+ }
19172
+ if (access.reason === "peer:cross_team_grant_missing") {
19173
+ crossTeamPeerAuditClient?.emit({
19174
+ event_type: "slack.peer.drop.cross_team_grant_missing",
19175
+ peer_agent_id: null,
19176
+ gate_path: null,
19177
+ grant_id: null,
19178
+ chat_id: evt.channel ?? null,
19179
+ message_id: evt.ts ?? null
19180
+ });
19181
+ }
19085
19182
  return;
19086
19183
  }
19087
- if (isRestartCommand) {
19088
- const senderAllowed = isRestartSenderAllowed(ALLOWED_USERS, evt.user);
19089
- if (!senderAllowed) {
19090
- await denyUnauthorizedRestart({
19184
+ if (access.kind === "command") {
19185
+ if (access.command === "help") {
19186
+ await handleHelpCommand({
19091
19187
  channel: evt.channel ?? "",
19092
- threadTs: evt.thread_ts
19188
+ threadTs: evt.thread_ts,
19189
+ ts: evt.ts ?? ""
19093
19190
  });
19094
19191
  return;
19095
19192
  }
19096
- const resolvedName = await resolveUserName(evt.user);
19097
- const requesterName = resolvedName && resolvedName !== evt.user ? resolvedName : void 0;
19098
- await handleRestartCommand({
19099
- channel: evt.channel ?? "",
19100
- // Only carry thread_ts when the originating message was already
19101
- // threaded; a top-level command acks in-channel rather than
19102
- // synthesising a brand-new thread (CodeRabbit feedback).
19103
- threadTs: evt.thread_ts,
19104
- ts: evt.ts ?? "",
19105
- requesterName
19106
- });
19107
- return;
19108
- }
19109
- if (evt.bot_id) {
19110
- const peerMsg = {
19111
- text: evt.text,
19112
- channel: evt.channel ?? "",
19113
- channel_type: evt.channel_type,
19114
- user: evt.user,
19115
- bot_id: evt.bot_id,
19116
- thread_ts: evt.thread_ts,
19117
- parent_user_id: evt.parent_user_id
19118
- };
19119
- const decision = classifyPeerMessage(
19120
- peerMsg,
19121
- SLACK_PEER_CLASSIFIER_CONFIG,
19122
- { bot_user_id: botUserId }
19123
- );
19124
- if (decision.kind === "self") return;
19125
- if (decision.kind === "drop") {
19126
- const channelHash = createHash("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
19127
- process.stderr.write(
19128
- `slack-channel: peer drop reason=${decision.reason} channel=${channelHash}
19129
- `
19130
- );
19131
- if (decision.reason === "cross_team_grant_missing") {
19132
- crossTeamPeerAuditClient?.emit({
19133
- event_type: "slack.peer.drop.cross_team_grant_missing",
19134
- peer_agent_id: null,
19135
- gate_path: null,
19136
- grant_id: null,
19137
- chat_id: evt.channel ?? null,
19138
- message_id: evt.ts ?? null
19193
+ if (access.command === "restart") {
19194
+ if (!access.authorized) {
19195
+ await denyUnauthorizedRestart({
19196
+ channel: evt.channel ?? "",
19197
+ threadTs: evt.thread_ts
19139
19198
  });
19199
+ return;
19140
19200
  }
19201
+ const resolvedName = await resolveUserName(evt.user);
19202
+ const requesterName = resolvedName && resolvedName !== evt.user ? resolvedName : void 0;
19203
+ await handleRestartCommand({
19204
+ channel: evt.channel ?? "",
19205
+ // Only carry thread_ts when the originating message was already
19206
+ // threaded; a top-level command acks in-channel.
19207
+ threadTs: evt.thread_ts,
19208
+ ts: evt.ts ?? "",
19209
+ requesterName
19210
+ });
19141
19211
  return;
19142
19212
  }
19143
- if (decision.kind === "peer-ingress") {
19144
- const peerGate = decision.peer.gate_path;
19145
- if (peerGate === "intra_org_unrestricted") {
19146
- crossTeamPeerAuditClient?.emit({
19147
- event_type: "slack.peer.ingress.cross_team",
19148
- peer_agent_id: decision.peer.agent_id || null,
19149
- gate_path: peerGate,
19150
- grant_id: null,
19151
- chat_id: evt.channel ?? null,
19152
- message_id: evt.ts ?? null
19153
- });
19154
- } else if (typeof peerGate === "string" && peerGate.startsWith("grant:")) {
19155
- crossTeamPeerAuditClient?.emit({
19156
- event_type: "slack.peer.ingress.cross_team",
19157
- peer_agent_id: decision.peer.agent_id || null,
19158
- gate_path: peerGate,
19159
- grant_id: peerGate.slice("grant:".length),
19160
- chat_id: evt.channel ?? null,
19161
- message_id: evt.ts ?? null
19162
- });
19163
- }
19213
+ return;
19214
+ }
19215
+ const isDirectMessage = evt.channel?.startsWith("D");
19216
+ const isThreadReply = !!evt.thread_ts && evt.thread_ts !== evt.ts;
19217
+ const trackTs = evt.thread_ts ?? evt.ts ?? "";
19218
+ const threadKey = buildThreadKey(evt.channel, trackTs);
19219
+ if (isBot && peerClassification?.kind === "peer-ingress") {
19220
+ const peerGate = peerClassification.peer.gate_path;
19221
+ if (peerGate === "intra_org_unrestricted") {
19222
+ crossTeamPeerAuditClient?.emit({
19223
+ event_type: "slack.peer.ingress.cross_team",
19224
+ peer_agent_id: peerClassification.peer.agent_id || null,
19225
+ gate_path: peerGate,
19226
+ grant_id: null,
19227
+ chat_id: evt.channel ?? null,
19228
+ message_id: evt.ts ?? null
19229
+ });
19230
+ } else if (typeof peerGate === "string" && peerGate.startsWith("grant:")) {
19231
+ crossTeamPeerAuditClient?.emit({
19232
+ event_type: "slack.peer.ingress.cross_team",
19233
+ peer_agent_id: peerClassification.peer.agent_id || null,
19234
+ gate_path: peerGate,
19235
+ grant_id: peerGate.slice("grant:".length),
19236
+ chat_id: evt.channel ?? null,
19237
+ message_id: evt.ts ?? null
19238
+ });
19164
19239
  }
19165
19240
  }
19166
19241
  if (evt.type === "app_mention") {
@@ -14430,6 +14430,45 @@ function readDeclineCooldownMs(envVarName, defaultSeconds = 1800) {
14430
14430
  return parsed * 1e3;
14431
14431
  }
14432
14432
 
14433
+ // src/inbound-access.ts
14434
+ function decideInboundAccess(input) {
14435
+ if (!input.content.forward) {
14436
+ return { kind: "drop", reason: `content:${input.content.reason}` };
14437
+ }
14438
+ if (input.isSelf) {
14439
+ return { kind: "drop", reason: "self" };
14440
+ }
14441
+ if (input.orgBoundary && !input.orgBoundary.forward) {
14442
+ return { kind: "drop", reason: "org_boundary" };
14443
+ }
14444
+ if (input.senderPolicy && !input.senderPolicy.forward) {
14445
+ const reason = input.senderPolicy.reason;
14446
+ const declineWorthy = input.sameOrg && reason !== "internal_only";
14447
+ return {
14448
+ kind: "drop",
14449
+ reason: `sender_policy:${reason}`,
14450
+ decline: declineWorthy ? politeDeclineCopy(reason) : void 0
14451
+ };
14452
+ }
14453
+ if (input.command && !input.isBot) {
14454
+ return {
14455
+ kind: "command",
14456
+ command: input.command.command,
14457
+ authorized: input.command.authorized
14458
+ };
14459
+ }
14460
+ if (input.isBot) {
14461
+ const peer = input.peer;
14462
+ if (!peer || peer.kind === "self") {
14463
+ return { kind: "drop", reason: peer?.kind === "self" ? "self" : "peer:unclassified" };
14464
+ }
14465
+ if (peer.kind === "drop") {
14466
+ return { kind: "drop", reason: `peer:${peer.reason ?? "unspecified"}` };
14467
+ }
14468
+ }
14469
+ return { kind: "admit" };
14470
+ }
14471
+
14433
14472
  // src/teams-peer-classifier.ts
14434
14473
  function classifyTeamsPeerMessage(input) {
14435
14474
  const { activity, config: config2 } = input;
@@ -14901,34 +14940,8 @@ async function processPendingFile(filename) {
14901
14940
  } catch {
14902
14941
  return;
14903
14942
  }
14904
- if (!decision.forward) {
14905
- process.stderr.write(
14906
- `teams-channel: dropped activity id=${activity.id ?? "?"} reason=${decision.reason}
14907
- `
14908
- );
14909
- return;
14910
- }
14911
14943
  const senderPolicyDecision = decideSenderPolicyForwardTeams(activity, MSTEAMS_SENDER_POLICY);
14912
- if (!senderPolicyDecision.forward) {
14913
- const subReason = classifyTeamsPolicyBlock(activity, MSTEAMS_SENDER_POLICY);
14914
- process.stderr.write(
14915
- `teams-channel: dropped activity id=${activity.id ?? "?"} reason=sender_policy sub=${subReason} mode=${MSTEAMS_SENDER_POLICY.mode}
14916
- `
14917
- );
14918
- await maybeSendTeamsSenderPolicyDecline({
14919
- serviceUrl: activity.serviceUrl,
14920
- conversationId: activity.conversation?.id,
14921
- replyToId: activity.id,
14922
- senderId: activity.from?.aadObjectId ?? activity.from?.id,
14923
- subReason
14924
- }).catch((err) => {
14925
- process.stderr.write(
14926
- `teams-channel: decline reply failed: ${err.message}
14927
- `
14928
- );
14929
- });
14930
- return;
14931
- }
14944
+ const policyBlockReason = senderPolicyDecision.forward ? void 0 : classifyTeamsPolicyBlock(activity, MSTEAMS_SENDER_POLICY);
14932
14945
  const peerDecision = classifyTeamsPeerMessage({
14933
14946
  activity,
14934
14947
  config: {
@@ -14937,11 +14950,42 @@ async function processPendingFile(filename) {
14937
14950
  mode: PEER_AGENT_MODE
14938
14951
  }
14939
14952
  });
14940
- if (peerDecision.classification === "peer_agent" && peerDecision.action === "drop") {
14953
+ const peerKind = peerDecision.classification === "peer_agent" && peerDecision.action === "drop" ? "drop" : peerDecision.classification === "peer_agent" ? "peer-ingress" : "human";
14954
+ const senderTenantId = activity.channelData?.tenant?.id ?? activity.conversation?.tenantId;
14955
+ const homeTenantId = process.env.MSTEAMS_HOME_TENANT_ID;
14956
+ const sameOrg = !!homeTenantId && homeTenantId !== "common" && senderTenantId === homeTenantId;
14957
+ const access = decideInboundAccess({
14958
+ content: decision.forward ? { forward: true } : { forward: false, reason: decision.reason },
14959
+ isSelf: false,
14960
+ // self-drop handled inside decideTeamsActivityForward
14961
+ isBot: true,
14962
+ // the peer classifier ran for every activity; 'human' falls through
14963
+ peer: {
14964
+ kind: peerKind,
14965
+ reason: "reason" in peerDecision ? peerDecision.reason : void 0
14966
+ },
14967
+ senderPolicy: senderPolicyDecision.forward ? { forward: true } : { forward: false, reason: policyBlockReason },
14968
+ sameOrg
14969
+ });
14970
+ if (access.kind === "drop") {
14941
14971
  process.stderr.write(
14942
- `teams-channel: dropped peer activity id=${activity.id ?? "?"} reason=${peerDecision.reason}
14972
+ `teams-channel: inbound drop id=${activity.id ?? "?"} reason=${access.reason}
14943
14973
  `
14944
14974
  );
14975
+ if (access.decline !== void 0 && policyBlockReason) {
14976
+ await maybeSendTeamsSenderPolicyDecline({
14977
+ serviceUrl: activity.serviceUrl,
14978
+ conversationId: activity.conversation?.id,
14979
+ replyToId: activity.id,
14980
+ senderId: activity.from?.aadObjectId ?? activity.from?.id,
14981
+ subReason: policyBlockReason
14982
+ }).catch((err) => {
14983
+ process.stderr.write(
14984
+ `teams-channel: decline reply failed: ${err.message}
14985
+ `
14986
+ );
14987
+ });
14988
+ }
14945
14989
  return;
14946
14990
  }
14947
14991
  if (activity.serviceUrl && activity.conversation?.id) {