@integrity-labs/agt-cli 0.27.144 → 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.
@@ -14813,6 +14813,61 @@ function parsePeerAgentModeEnv(raw) {
14813
14813
  return "off";
14814
14814
  }
14815
14815
 
14816
+ // src/sender-policy-decline.ts
14817
+ function politeDeclineCopy(reason) {
14818
+ switch (reason) {
14819
+ case "internal_only":
14820
+ return "Sorry, I can only respond to people inside our organisation. This conversation appears to be from outside.";
14821
+ case "mode_manager_only":
14822
+ return "Sorry, I only respond to my manager in this channel.";
14823
+ case "mode_team_only":
14824
+ return "Sorry, this agent only replies to its team members.";
14825
+ case "mode_team_agents_only":
14826
+ return "Sorry, I only respond to agents on my team in this channel.";
14827
+ case "mode_agents_only":
14828
+ return "Sorry, I only respond to other agents in this channel.";
14829
+ }
14830
+ }
14831
+
14832
+ // src/inbound-access.ts
14833
+ function decideInboundAccess(input) {
14834
+ if (!input.content.forward) {
14835
+ return { kind: "drop", reason: `content:${input.content.reason}` };
14836
+ }
14837
+ if (input.isSelf) {
14838
+ return { kind: "drop", reason: "self" };
14839
+ }
14840
+ if (input.orgBoundary && !input.orgBoundary.forward) {
14841
+ return { kind: "drop", reason: "org_boundary" };
14842
+ }
14843
+ if (input.senderPolicy && !input.senderPolicy.forward) {
14844
+ const reason = input.senderPolicy.reason;
14845
+ const declineWorthy = input.sameOrg && reason !== "internal_only";
14846
+ return {
14847
+ kind: "drop",
14848
+ reason: `sender_policy:${reason}`,
14849
+ decline: declineWorthy ? politeDeclineCopy(reason) : void 0
14850
+ };
14851
+ }
14852
+ if (input.command && !input.isBot) {
14853
+ return {
14854
+ kind: "command",
14855
+ command: input.command.command,
14856
+ authorized: input.command.authorized
14857
+ };
14858
+ }
14859
+ if (input.isBot) {
14860
+ const peer = input.peer;
14861
+ if (!peer || peer.kind === "self") {
14862
+ return { kind: "drop", reason: peer?.kind === "self" ? "self" : "peer:unclassified" };
14863
+ }
14864
+ if (peer.kind === "drop") {
14865
+ return { kind: "drop", reason: `peer:${peer.reason ?? "unspecified"}` };
14866
+ }
14867
+ }
14868
+ return { kind: "admit" };
14869
+ }
14870
+
14816
14871
  // src/telegram-peer-rate-limiter.ts
14817
14872
  var SECOND_MS = 1e3;
14818
14873
  var MINUTE_MS = 60 * SECOND_MS;
@@ -18108,14 +18163,68 @@ async function pollLoop() {
18108
18163
  const content = typeof msg.text === "string" && msg.text.length > 0 ? msg.text : typeof msg.caption === "string" && msg.caption.length > 0 ? msg.caption : "";
18109
18164
  if (content.length === 0 && classifiedAttachments.length === 0) continue;
18110
18165
  const chatId = String(msg.chat.id);
18111
- if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId)) continue;
18166
+ const trimmedContent = content.trim();
18167
+ const isFromBot = !!msg.from?.is_bot;
18168
+ const nowMs = Date.now();
18169
+ const telegramCommand = isHelpSyntax(trimmedContent) ? "help" : isStatusSyntax(trimmedContent) ? "status" : isRestartSyntax(trimmedContent) ? "restart" : isInvestigateSyntax(trimmedContent) ? "investigate" : void 0;
18170
+ let classification;
18171
+ let peerLeaf;
18172
+ if (isFromBot) {
18173
+ if (PEER_DISABLED_GLOBAL) {
18174
+ peerLeaf = { kind: "drop", reason: "peer_disabled_global" };
18175
+ } else {
18176
+ const classifierMsg = {
18177
+ message_id: msg.message_id,
18178
+ text: msg.text,
18179
+ caption: msg.caption,
18180
+ from: msg.from ? { id: msg.from.id, username: msg.from.username, is_bot: msg.from.is_bot } : void 0,
18181
+ chat: { id: msg.chat.id, type: msg.chat.type },
18182
+ reply_to_message: msg.reply_to_message ? {
18183
+ message_id: msg.reply_to_message.message_id,
18184
+ from: msg.reply_to_message.from ? { id: msg.reply_to_message.from.id, is_bot: msg.reply_to_message.from.is_bot } : void 0
18185
+ } : void 0,
18186
+ entities: msg.entities,
18187
+ caption_entities: msg.caption_entities
18188
+ };
18189
+ const self = await resolveBotIdentity();
18190
+ classification = classifyPeerMessage(classifierMsg, PEER_CLASSIFIER_CONFIG, self);
18191
+ peerLeaf = {
18192
+ kind: classification.kind,
18193
+ reason: classification.kind === "drop" ? classification.reason : void 0
18194
+ };
18195
+ }
18196
+ }
18197
+ const access = decideInboundAccess({
18198
+ content: { forward: true },
18199
+ // empty content already filtered above
18200
+ // CodeRabbit: derive self from the peer classification so an echo of
18201
+ // our own outbound is dropped at the self step — BEFORE command
18202
+ // routing — instead of slipping through as a command. (Slack catches
18203
+ // self at evt.user===botUserId and Teams inside its activity filter;
18204
+ // Telegram's only self signal is the peer classifier.)
18205
+ isSelf: peerLeaf?.kind === "self",
18206
+ isBot: isFromBot,
18207
+ peer: peerLeaf,
18208
+ // TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis (the drift
18209
+ // the consolidation names). An unlisted chat is external ⇒ silent.
18210
+ orgBoundary: ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId) ? { forward: false } : { forward: true },
18211
+ command: telegramCommand ? { command: telegramCommand, authorized: true } : void 0,
18212
+ // No sender-policy mode system on Telegram, so no same-org decline.
18213
+ sameOrg: false
18214
+ });
18215
+ if (access.kind === "drop" && (access.reason === "org_boundary" || access.reason.startsWith("content:"))) {
18216
+ process.stderr.write(
18217
+ `telegram-channel(${AGENT_CODE_NAME}): inbound drop reason=${access.reason} chat=${redactId(chatId)}
18218
+ `
18219
+ );
18220
+ continue;
18221
+ }
18112
18222
  observedChatClient?.observe({
18113
18223
  chat_id: chatId,
18114
18224
  chat_title: msg.chat.title ?? msg.chat.username ?? msg.chat.first_name ?? null,
18115
18225
  chat_type: msg.chat.type ?? null
18116
18226
  });
18117
- const trimmedContent = content.trim();
18118
- if (isHelpSyntax(trimmedContent)) {
18227
+ if (access.kind === "command" && access.command === "help") {
18119
18228
  const disposition = await classifyRestartCommand(trimmedContent.replace(/^\/help/, "/restart"));
18120
18229
  if (disposition === "act") {
18121
18230
  await handleHelpCommand({
@@ -18125,7 +18234,7 @@ async function pollLoop() {
18125
18234
  }
18126
18235
  continue;
18127
18236
  }
18128
- if (isStatusSyntax(trimmedContent)) {
18237
+ if (access.kind === "command" && access.command === "status") {
18129
18238
  const disposition = await classifyRestartCommand(
18130
18239
  trimmedContent.replace(/^\/status/, "/restart")
18131
18240
  );
@@ -18137,7 +18246,7 @@ async function pollLoop() {
18137
18246
  }
18138
18247
  continue;
18139
18248
  }
18140
- if (isRestartSyntax(trimmedContent)) {
18249
+ if (access.kind === "command" && access.command === "restart") {
18141
18250
  const disposition = await classifyRestartCommand(trimmedContent);
18142
18251
  if (disposition === "act") {
18143
18252
  await handleRestartCommand({
@@ -18166,7 +18275,7 @@ async function pollLoop() {
18166
18275
  }
18167
18276
  continue;
18168
18277
  }
18169
- if (isInvestigateSyntax(trimmedContent)) {
18278
+ if (access.kind === "command" && access.command === "investigate") {
18170
18279
  const disposition = await classifyInvestigateCommand(trimmedContent);
18171
18280
  if (disposition === "act") {
18172
18281
  await handleInvestigateCommand({
@@ -18197,8 +18306,6 @@ async function pollLoop() {
18197
18306
  }
18198
18307
  const userId = msg.from?.id != null ? String(msg.from.id) : "unknown";
18199
18308
  const userName = msg.from?.username || [msg.from?.first_name, msg.from?.last_name].filter(Boolean).join(" ").trim() || userId;
18200
- const isFromBot = !!msg.from?.is_bot;
18201
- const nowMs = Date.now();
18202
18309
  if (chatId && !isFromBot) {
18203
18310
  resetThread(chatId, "");
18204
18311
  }
@@ -18207,38 +18314,14 @@ async function pollLoop() {
18207
18314
  peerRateLimiter.recordInboundHuman(chatId, nowMs);
18208
18315
  }
18209
18316
  let peerAgentMeta = null;
18210
- if (isFromBot) {
18211
- if (PEER_DISABLED_GLOBAL) {
18317
+ if (access.kind === "drop") {
18318
+ if (access.reason !== "self") {
18319
+ const peerReason = access.reason.startsWith("peer:") ? access.reason.slice("peer:".length) : access.reason;
18212
18320
  process.stderr.write(
18213
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=peer_disabled_global chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18321
+ `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${peerReason} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18214
18322
  `
18215
18323
  );
18216
- continue;
18217
- }
18218
- const classifierMsg = {
18219
- message_id: msg.message_id,
18220
- text: msg.text,
18221
- caption: msg.caption,
18222
- from: msg.from ? { id: msg.from.id, username: msg.from.username, is_bot: msg.from.is_bot } : void 0,
18223
- chat: { id: msg.chat.id, type: msg.chat.type },
18224
- reply_to_message: msg.reply_to_message ? {
18225
- message_id: msg.reply_to_message.message_id,
18226
- from: msg.reply_to_message.from ? { id: msg.reply_to_message.from.id, is_bot: msg.reply_to_message.from.is_bot } : void 0
18227
- } : void 0,
18228
- entities: msg.entities,
18229
- caption_entities: msg.caption_entities
18230
- };
18231
- const self = await resolveBotIdentity();
18232
- const classification = classifyPeerMessage(classifierMsg, PEER_CLASSIFIER_CONFIG, self);
18233
- if (classification.kind === "self") {
18234
- continue;
18235
- }
18236
- if (classification.kind === "drop") {
18237
- process.stderr.write(
18238
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${classification.reason} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18239
- `
18240
- );
18241
- if (classification.reason === "cross_team_grant_missing") {
18324
+ if (classification?.kind === "drop" && classification.reason === "cross_team_grant_missing") {
18242
18325
  crossTeamPeerAuditClient?.emit({
18243
18326
  event_type: "telegram.peer.drop.cross_team_grant_missing",
18244
18327
  peer_agent_id: null,
@@ -18248,52 +18331,52 @@ async function pollLoop() {
18248
18331
  message_id: String(msg.message_id)
18249
18332
  });
18250
18333
  }
18251
- continue;
18252
18334
  }
18253
- if (classification.kind === "peer-ingress") {
18254
- const limit = await peerRateLimiter.recordInboundPeer(
18255
- chatId,
18256
- classification.peer.bot_id,
18257
- nowMs
18258
- );
18259
- if (limit.decision !== "ok") {
18260
- process.stderr.write(
18261
- `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${limit.decision} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18335
+ continue;
18336
+ }
18337
+ if (isFromBot && classification?.kind === "peer-ingress") {
18338
+ const limit = await peerRateLimiter.recordInboundPeer(
18339
+ chatId,
18340
+ classification.peer.bot_id,
18341
+ nowMs
18342
+ );
18343
+ if (limit.decision !== "ok") {
18344
+ process.stderr.write(
18345
+ `telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${limit.decision} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
18262
18346
  `
18263
- );
18264
- continue;
18265
- }
18266
- if (limit.dailyBudgetWarn && !dailyBudgetWarned) {
18267
- dailyBudgetWarned = true;
18268
- process.stderr.write(
18269
- `telegram-channel(${AGENT_CODE_NAME}): peer_warn reason=daily_budget_80pct
18347
+ );
18348
+ continue;
18349
+ }
18350
+ if (limit.dailyBudgetWarn && !dailyBudgetWarned) {
18351
+ dailyBudgetWarned = true;
18352
+ process.stderr.write(
18353
+ `telegram-channel(${AGENT_CODE_NAME}): peer_warn reason=daily_budget_80pct
18270
18354
  `
18271
- );
18272
- }
18273
- peerAgentMeta = {
18274
- code_name: classification.peer.code_name,
18275
- agent_id: classification.peer.agent_id
18276
- };
18277
- const gate = classification.peer.gate_path;
18278
- if (gate && gate !== "same_team" && gate !== "intra_org_unrestricted" && gate.startsWith("grant:")) {
18279
- crossTeamPeerAuditClient?.emit({
18280
- event_type: "telegram.peer.ingress.cross_team",
18281
- peer_agent_id: classification.peer.agent_id || null,
18282
- gate_path: gate,
18283
- grant_id: gate.slice("grant:".length),
18284
- chat_id: String(chatId),
18285
- message_id: String(msg.message_id)
18286
- });
18287
- } else if (gate === "intra_org_unrestricted") {
18288
- crossTeamPeerAuditClient?.emit({
18289
- event_type: "telegram.peer.ingress.cross_team",
18290
- peer_agent_id: classification.peer.agent_id || null,
18291
- gate_path: gate,
18292
- grant_id: null,
18293
- chat_id: String(chatId),
18294
- message_id: String(msg.message_id)
18295
- });
18296
- }
18355
+ );
18356
+ }
18357
+ peerAgentMeta = {
18358
+ code_name: classification.peer.code_name,
18359
+ agent_id: classification.peer.agent_id
18360
+ };
18361
+ const gate = classification.peer.gate_path;
18362
+ if (gate && gate !== "same_team" && gate !== "intra_org_unrestricted" && gate.startsWith("grant:")) {
18363
+ crossTeamPeerAuditClient?.emit({
18364
+ event_type: "telegram.peer.ingress.cross_team",
18365
+ peer_agent_id: classification.peer.agent_id || null,
18366
+ gate_path: gate,
18367
+ grant_id: gate.slice("grant:".length),
18368
+ chat_id: String(chatId),
18369
+ message_id: String(msg.message_id)
18370
+ });
18371
+ } else if (gate === "intra_org_unrestricted") {
18372
+ crossTeamPeerAuditClient?.emit({
18373
+ event_type: "telegram.peer.ingress.cross_team",
18374
+ peer_agent_id: classification.peer.agent_id || null,
18375
+ gate_path: gate,
18376
+ grant_id: null,
18377
+ chat_id: String(chatId),
18378
+ message_id: String(msg.message_id)
18379
+ });
18297
18380
  }
18298
18381
  }
18299
18382
  const messageId = String(msg.message_id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.27.144",
3
+ "version": "0.27.145",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsup && npm run build:mcp-assets && npm run build:cli-assets",
26
- "build:mcp-assets": "mkdir -p dist/mcp && cp ../../packages/mcp/dist/index.js dist/mcp/index.js && cp ../../packages/mcp/dist/slack-channel.js dist/mcp/slack-channel.js && cp ../../packages/mcp/dist/direct-chat-channel.js dist/mcp/direct-chat-channel.js && cp ../../packages/mcp/dist/telegram-channel.js dist/mcp/telegram-channel.js && cp ../../packages/mcp/dist/teams-channel.js dist/mcp/teams-channel.js",
26
+ "build:mcp-assets": "mkdir -p dist/mcp && cp ../../packages/mcp/dist/index.js dist/mcp/index.js && cp ../../packages/mcp/dist/slack-channel.js dist/mcp/slack-channel.js && cp ../../packages/mcp/dist/direct-chat-channel.js dist/mcp/direct-chat-channel.js && cp ../../packages/mcp/dist/telegram-channel.js dist/mcp/telegram-channel.js && cp ../../packages/mcp/dist/teams-channel.js dist/mcp/teams-channel.js && cp ../../packages/augmented-admin-mcp/dist/index.js dist/mcp/augmented-admin.js",
27
27
  "build:cli-assets": "mkdir -p dist/assets && cp assets/impersonate-statusline.sh dist/assets/impersonate-statusline.sh && chmod +x dist/assets/impersonate-statusline.sh",
28
28
  "dev": "tsx watch src/bin/agt.ts",
29
29
  "test": "vitest run",
@@ -52,6 +52,7 @@
52
52
  "devDependencies": {
53
53
  "@augmented/core": "workspace:*",
54
54
  "@integrity-labs/augmented-mcp": "workspace:*",
55
+ "@integrity-labs/augmented-admin-mcp": "workspace:*",
55
56
  "@types/node": "22.19.11",
56
57
  "@types/react": "19.2.14",
57
58
  "@types/ws": "8.18.1",