@integrity-labs/agt-cli 0.19.19 → 0.19.21
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.
- package/dist/bin/agt.js +78 -3
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-WQJL6EGC.js → chunk-5WDQ5G5M.js} +367 -16
- package/dist/chunk-5WDQ5G5M.js.map +1 -0
- package/dist/lib/manager-worker.js +98 -17
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/slack-channel.js +282 -0
- package/mcp/telegram-channel.js +270 -8
- package/package.json +1 -1
- package/dist/chunk-WQJL6EGC.js.map +0 -1
package/mcp/slack-channel.js
CHANGED
|
@@ -14734,6 +14734,202 @@ function buildChannelInfoResult(args) {
|
|
|
14734
14734
|
return { ok: true, channel, bot_user_handle: botUserHandle };
|
|
14735
14735
|
}
|
|
14736
14736
|
|
|
14737
|
+
// src/slack-peer-classifier.ts
|
|
14738
|
+
var CODE_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
14739
|
+
function classifyPeerMessage(msg, cfg, self) {
|
|
14740
|
+
if (!msg.bot_id) return { kind: "human" };
|
|
14741
|
+
if (self.bot_user_id !== null && msg.user === self.bot_user_id) {
|
|
14742
|
+
return { kind: "self" };
|
|
14743
|
+
}
|
|
14744
|
+
if (cfg.peer_disabled_mode === "all") {
|
|
14745
|
+
return { kind: "drop", reason: "peer_disabled_all" };
|
|
14746
|
+
}
|
|
14747
|
+
if (cfg.peer_agent_mode === "off") {
|
|
14748
|
+
return { kind: "drop", reason: "mode_off" };
|
|
14749
|
+
}
|
|
14750
|
+
if (cfg.peer_group_ids.length === 0) {
|
|
14751
|
+
if (msg.channel_type !== "im") {
|
|
14752
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
14753
|
+
}
|
|
14754
|
+
} else if (!cfg.peer_group_ids.includes(msg.channel)) {
|
|
14755
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
14756
|
+
}
|
|
14757
|
+
const peer = cfg.peers.find((p) => p.bot_user_id === msg.user);
|
|
14758
|
+
if (!peer) {
|
|
14759
|
+
return { kind: "drop", reason: "unknown_peer" };
|
|
14760
|
+
}
|
|
14761
|
+
if (peer.gate_path === null) {
|
|
14762
|
+
return { kind: "drop", reason: "cross_team_grant_missing" };
|
|
14763
|
+
}
|
|
14764
|
+
if (cfg.peer_disabled_mode === "cross_team_only" && peer.gate_path !== void 0 && peer.gate_path !== "same_team") {
|
|
14765
|
+
return { kind: "drop", reason: "peer_disabled_cross_team" };
|
|
14766
|
+
}
|
|
14767
|
+
if (self.bot_user_id === null) {
|
|
14768
|
+
return { kind: "drop", reason: "self_resolution_pending" };
|
|
14769
|
+
}
|
|
14770
|
+
const addressing = classifyAddressing(msg, self.bot_user_id);
|
|
14771
|
+
if (!addressing.addressed) {
|
|
14772
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
14773
|
+
}
|
|
14774
|
+
if (addressing.viaReplyOnly) {
|
|
14775
|
+
const inAllowedGroup = cfg.peer_group_ids.length > 0 && cfg.peer_group_ids.includes(msg.channel);
|
|
14776
|
+
const respondMode = cfg.peer_agent_mode === "respond";
|
|
14777
|
+
if (!(inAllowedGroup && respondMode)) {
|
|
14778
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
14779
|
+
}
|
|
14780
|
+
}
|
|
14781
|
+
return { kind: "peer-ingress", peer };
|
|
14782
|
+
}
|
|
14783
|
+
function classifyAddressing(msg, ourBotUserId) {
|
|
14784
|
+
const text = msg.text ?? "";
|
|
14785
|
+
const mentionToken = `<@${ourBotUserId}>`;
|
|
14786
|
+
const viaMention = text.includes(mentionToken);
|
|
14787
|
+
const viaReply = !!msg.thread_ts && msg.thread_ts !== void 0 && msg.parent_user_id === ourBotUserId;
|
|
14788
|
+
if (viaMention) {
|
|
14789
|
+
return { addressed: true, viaReplyOnly: false };
|
|
14790
|
+
}
|
|
14791
|
+
if (viaReply) {
|
|
14792
|
+
return { addressed: true, viaReplyOnly: true };
|
|
14793
|
+
}
|
|
14794
|
+
return { addressed: false, viaReplyOnly: false };
|
|
14795
|
+
}
|
|
14796
|
+
function parsePeersEnv(raw, gateRaw) {
|
|
14797
|
+
if (!raw || raw.trim().length === 0) return [];
|
|
14798
|
+
let parsed;
|
|
14799
|
+
try {
|
|
14800
|
+
parsed = JSON.parse(raw);
|
|
14801
|
+
} catch {
|
|
14802
|
+
return [];
|
|
14803
|
+
}
|
|
14804
|
+
if (!Array.isArray(parsed)) return [];
|
|
14805
|
+
const gateMap = /* @__PURE__ */ new Map();
|
|
14806
|
+
let gateConfigInvalid = false;
|
|
14807
|
+
if (gateRaw && gateRaw.trim().length > 0) {
|
|
14808
|
+
try {
|
|
14809
|
+
const gateParsed = JSON.parse(gateRaw);
|
|
14810
|
+
if (!gateParsed || typeof gateParsed !== "object" || Array.isArray(gateParsed)) {
|
|
14811
|
+
gateConfigInvalid = true;
|
|
14812
|
+
} else {
|
|
14813
|
+
for (const [k, v] of Object.entries(gateParsed)) {
|
|
14814
|
+
if (v === null) {
|
|
14815
|
+
gateMap.set(k, null);
|
|
14816
|
+
} else if (typeof v === "string") {
|
|
14817
|
+
const isGrantPath = v.startsWith("grant:") && v.slice("grant:".length).trim().length > 0;
|
|
14818
|
+
if (v === "same_team" || v === "intra_org_unrestricted" || isGrantPath) {
|
|
14819
|
+
gateMap.set(k, v);
|
|
14820
|
+
} else {
|
|
14821
|
+
gateConfigInvalid = true;
|
|
14822
|
+
break;
|
|
14823
|
+
}
|
|
14824
|
+
} else {
|
|
14825
|
+
gateConfigInvalid = true;
|
|
14826
|
+
break;
|
|
14827
|
+
}
|
|
14828
|
+
}
|
|
14829
|
+
}
|
|
14830
|
+
} catch {
|
|
14831
|
+
gateConfigInvalid = true;
|
|
14832
|
+
}
|
|
14833
|
+
if (gateConfigInvalid) {
|
|
14834
|
+
console.error(
|
|
14835
|
+
"[slack-peer-classifier] SLACK_PEERS_GATE is present but malformed; failing closed (every peer drops as cross_team_grant_missing)"
|
|
14836
|
+
);
|
|
14837
|
+
}
|
|
14838
|
+
}
|
|
14839
|
+
const out = [];
|
|
14840
|
+
for (const entry of parsed) {
|
|
14841
|
+
if (entry && typeof entry === "object" && typeof entry.code_name === "string" && CODE_NAME_RE.test(entry.code_name) && typeof entry.agent_id === "string" && typeof entry.bot_user_id === "string" && entry.bot_user_id.length > 0) {
|
|
14842
|
+
const e = entry;
|
|
14843
|
+
const peer = {
|
|
14844
|
+
code_name: e.code_name,
|
|
14845
|
+
bot_user_id: e.bot_user_id,
|
|
14846
|
+
agent_id: e.agent_id
|
|
14847
|
+
};
|
|
14848
|
+
if (gateConfigInvalid) {
|
|
14849
|
+
peer.gate_path = null;
|
|
14850
|
+
} else if (gateMap.has(e.bot_user_id)) {
|
|
14851
|
+
peer.gate_path = gateMap.get(e.bot_user_id) ?? null;
|
|
14852
|
+
}
|
|
14853
|
+
out.push(peer);
|
|
14854
|
+
}
|
|
14855
|
+
}
|
|
14856
|
+
return out;
|
|
14857
|
+
}
|
|
14858
|
+
function parsePeerGroupIdsEnv(raw) {
|
|
14859
|
+
if (!raw) return [];
|
|
14860
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
14861
|
+
}
|
|
14862
|
+
function parsePeerAgentModeEnv(raw) {
|
|
14863
|
+
if (raw === "listen" || raw === "respond") return raw;
|
|
14864
|
+
return "off";
|
|
14865
|
+
}
|
|
14866
|
+
|
|
14867
|
+
// src/cross-team-peer-audit-client.ts
|
|
14868
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
14869
|
+
function createCrossTeamPeerAuditClient(args) {
|
|
14870
|
+
if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
|
|
14871
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
14872
|
+
const log = args.log ?? (() => {
|
|
14873
|
+
});
|
|
14874
|
+
const base = args.agtHost.replace(/\/+$/, "");
|
|
14875
|
+
const agentId = args.agentId;
|
|
14876
|
+
const apiKey = args.agtApiKey;
|
|
14877
|
+
let cachedToken = null;
|
|
14878
|
+
let cachedTokenExpiresAt = 0;
|
|
14879
|
+
async function getToken() {
|
|
14880
|
+
if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
|
|
14881
|
+
const resp = await fetchImpl(`${base}/host/exchange`, {
|
|
14882
|
+
method: "POST",
|
|
14883
|
+
headers: { "Content-Type": "application/json" },
|
|
14884
|
+
body: JSON.stringify({ host_key: apiKey }),
|
|
14885
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
14886
|
+
});
|
|
14887
|
+
if (!resp.ok) {
|
|
14888
|
+
const body = await resp.text().catch(() => "");
|
|
14889
|
+
throw new Error(`/host/exchange failed (${resp.status}): ${body.slice(0, 200)}`);
|
|
14890
|
+
}
|
|
14891
|
+
const data = await resp.json();
|
|
14892
|
+
cachedToken = data.token;
|
|
14893
|
+
cachedTokenExpiresAt = data.expires_at ? new Date(data.expires_at).getTime() - 12e4 : Date.now() + 55 * 6e4;
|
|
14894
|
+
return cachedToken;
|
|
14895
|
+
}
|
|
14896
|
+
async function postOnce(event) {
|
|
14897
|
+
const token = await getToken();
|
|
14898
|
+
return fetchImpl(`${base}/host/cross-team-peer-event`, {
|
|
14899
|
+
method: "POST",
|
|
14900
|
+
headers: {
|
|
14901
|
+
Authorization: `Bearer ${token}`,
|
|
14902
|
+
"Content-Type": "application/json"
|
|
14903
|
+
},
|
|
14904
|
+
body: JSON.stringify({ agent_id: agentId, ...event }),
|
|
14905
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
14906
|
+
});
|
|
14907
|
+
}
|
|
14908
|
+
async function emitAsync(event) {
|
|
14909
|
+
try {
|
|
14910
|
+
let resp = await postOnce(event);
|
|
14911
|
+
if (resp.status === 401) {
|
|
14912
|
+
cachedToken = null;
|
|
14913
|
+
cachedTokenExpiresAt = 0;
|
|
14914
|
+
resp = await postOnce(event);
|
|
14915
|
+
}
|
|
14916
|
+
if (!resp.ok) {
|
|
14917
|
+
const body = await resp.text().catch(() => "");
|
|
14918
|
+
log(
|
|
14919
|
+
`cross-team-peer-audit: POST failed (${resp.status}) event=${event.event_type}: ${body.slice(0, 200)}`
|
|
14920
|
+
);
|
|
14921
|
+
}
|
|
14922
|
+
} catch (err) {
|
|
14923
|
+
log(`cross-team-peer-audit: emit threw event=${event.event_type}: ${err.message}`);
|
|
14924
|
+
}
|
|
14925
|
+
}
|
|
14926
|
+
return {
|
|
14927
|
+
emit(event) {
|
|
14928
|
+
void emitAsync(event);
|
|
14929
|
+
}
|
|
14930
|
+
};
|
|
14931
|
+
}
|
|
14932
|
+
|
|
14737
14933
|
// src/slack-channel.ts
|
|
14738
14934
|
var BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
|
|
14739
14935
|
var APP_TOKEN = process.env.SLACK_APP_TOKEN;
|
|
@@ -14749,6 +14945,35 @@ var ALLOWED_USERS = new Set(
|
|
|
14749
14945
|
);
|
|
14750
14946
|
var THREAD_AUTO_FOLLOW = process.env.SLACK_THREAD_AUTO_FOLLOW ?? "off";
|
|
14751
14947
|
var CHANNEL_RESPONSE_MODE = parseResponseMode(process.env.SLACK_CHANNEL_RESPONSE_MODE);
|
|
14948
|
+
var SLACK_PEER_DISABLED_MODE = (() => {
|
|
14949
|
+
const raw = (process.env.PEER_DISABLED ?? "").trim().toLowerCase();
|
|
14950
|
+
if (raw === "" || raw === "off") return "off";
|
|
14951
|
+
if (raw === "cross_team_only" || raw === "all") return raw;
|
|
14952
|
+
process.stderr.write(
|
|
14953
|
+
`slack-channel: invalid PEER_DISABLED=${JSON.stringify(process.env.PEER_DISABLED)} \u2014 defaulting to 'all' (fail-closed)
|
|
14954
|
+
`
|
|
14955
|
+
);
|
|
14956
|
+
return "all";
|
|
14957
|
+
})();
|
|
14958
|
+
if (SLACK_PEER_DISABLED_MODE !== "off") {
|
|
14959
|
+
process.stderr.write(
|
|
14960
|
+
`slack-channel: PEER_DISABLED=${SLACK_PEER_DISABLED_MODE} \u2014 ${SLACK_PEER_DISABLED_MODE === "all" ? "every peer surface short-circuited" : "cross-team peers dropped, same-team peers admitted"}
|
|
14961
|
+
`
|
|
14962
|
+
);
|
|
14963
|
+
}
|
|
14964
|
+
var crossTeamPeerAuditClient = createCrossTeamPeerAuditClient({
|
|
14965
|
+
agtHost: AGT_HOST,
|
|
14966
|
+
agtApiKey: AGT_API_KEY,
|
|
14967
|
+
agentId: AGT_AGENT_ID,
|
|
14968
|
+
log: (line) => process.stderr.write(`slack-channel: ${line}
|
|
14969
|
+
`)
|
|
14970
|
+
});
|
|
14971
|
+
var SLACK_PEER_CLASSIFIER_CONFIG = {
|
|
14972
|
+
peer_agent_mode: parsePeerAgentModeEnv(process.env.SLACK_PEER_AGENT_MODE),
|
|
14973
|
+
peer_group_ids: parsePeerGroupIdsEnv(process.env.SLACK_PEER_GROUP_IDS),
|
|
14974
|
+
peers: parsePeersEnv(process.env.SLACK_PEERS, process.env.SLACK_PEERS_GATE),
|
|
14975
|
+
peer_disabled_mode: SLACK_PEER_DISABLED_MODE
|
|
14976
|
+
};
|
|
14752
14977
|
var RESPONSE_TIMEOUT_MS = 3e5;
|
|
14753
14978
|
var pendingMessages = /* @__PURE__ */ new Map();
|
|
14754
14979
|
var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join2(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
@@ -16715,6 +16940,63 @@ async function connectSocketMode() {
|
|
|
16715
16940
|
return;
|
|
16716
16941
|
}
|
|
16717
16942
|
if (ALLOWED_USERS.size > 0 && evt.user && !ALLOWED_USERS.has(evt.user)) return;
|
|
16943
|
+
if (evt.bot_id) {
|
|
16944
|
+
const peerMsg = {
|
|
16945
|
+
text: evt.text,
|
|
16946
|
+
channel: evt.channel ?? "",
|
|
16947
|
+
channel_type: evt.channel_type,
|
|
16948
|
+
user: evt.user,
|
|
16949
|
+
bot_id: evt.bot_id,
|
|
16950
|
+
thread_ts: evt.thread_ts,
|
|
16951
|
+
parent_user_id: evt.parent_user_id
|
|
16952
|
+
};
|
|
16953
|
+
const decision = classifyPeerMessage(
|
|
16954
|
+
peerMsg,
|
|
16955
|
+
SLACK_PEER_CLASSIFIER_CONFIG,
|
|
16956
|
+
{ bot_user_id: botUserId }
|
|
16957
|
+
);
|
|
16958
|
+
if (decision.kind === "self") return;
|
|
16959
|
+
if (decision.kind === "drop") {
|
|
16960
|
+
const channelHash = createHash("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
|
|
16961
|
+
process.stderr.write(
|
|
16962
|
+
`slack-channel: peer drop reason=${decision.reason} channel=${channelHash}
|
|
16963
|
+
`
|
|
16964
|
+
);
|
|
16965
|
+
if (decision.reason === "cross_team_grant_missing") {
|
|
16966
|
+
crossTeamPeerAuditClient?.emit({
|
|
16967
|
+
event_type: "slack.peer.drop.cross_team_grant_missing",
|
|
16968
|
+
peer_agent_id: null,
|
|
16969
|
+
gate_path: null,
|
|
16970
|
+
grant_id: null,
|
|
16971
|
+
chat_id: evt.channel ?? null,
|
|
16972
|
+
message_id: evt.ts ?? null
|
|
16973
|
+
});
|
|
16974
|
+
}
|
|
16975
|
+
return;
|
|
16976
|
+
}
|
|
16977
|
+
if (decision.kind === "peer-ingress") {
|
|
16978
|
+
const peerGate = decision.peer.gate_path;
|
|
16979
|
+
if (peerGate === "intra_org_unrestricted") {
|
|
16980
|
+
crossTeamPeerAuditClient?.emit({
|
|
16981
|
+
event_type: "slack.peer.ingress.cross_team",
|
|
16982
|
+
peer_agent_id: decision.peer.agent_id || null,
|
|
16983
|
+
gate_path: peerGate,
|
|
16984
|
+
grant_id: null,
|
|
16985
|
+
chat_id: evt.channel ?? null,
|
|
16986
|
+
message_id: evt.ts ?? null
|
|
16987
|
+
});
|
|
16988
|
+
} else if (typeof peerGate === "string" && peerGate.startsWith("grant:")) {
|
|
16989
|
+
crossTeamPeerAuditClient?.emit({
|
|
16990
|
+
event_type: "slack.peer.ingress.cross_team",
|
|
16991
|
+
peer_agent_id: decision.peer.agent_id || null,
|
|
16992
|
+
gate_path: peerGate,
|
|
16993
|
+
grant_id: peerGate.slice("grant:".length),
|
|
16994
|
+
chat_id: evt.channel ?? null,
|
|
16995
|
+
message_id: evt.ts ?? null
|
|
16996
|
+
});
|
|
16997
|
+
}
|
|
16998
|
+
}
|
|
16999
|
+
}
|
|
16718
17000
|
if (evt.type === "app_mention") {
|
|
16719
17001
|
rememberThread(evt.channel, trackTs, "mentioned");
|
|
16720
17002
|
}
|
package/mcp/telegram-channel.js
CHANGED
|
@@ -14217,6 +14217,9 @@ function classifyPeerMessage(msg, cfg, self) {
|
|
|
14217
14217
|
if (self.bot_id !== null && msg.from.id === self.bot_id) {
|
|
14218
14218
|
return { kind: "self" };
|
|
14219
14219
|
}
|
|
14220
|
+
if (cfg.peer_disabled_mode === "all") {
|
|
14221
|
+
return { kind: "drop", reason: "peer_disabled_all" };
|
|
14222
|
+
}
|
|
14220
14223
|
if (cfg.peer_agent_mode === "off") {
|
|
14221
14224
|
return { kind: "drop", reason: "mode_off" };
|
|
14222
14225
|
}
|
|
@@ -14232,6 +14235,12 @@ function classifyPeerMessage(msg, cfg, self) {
|
|
|
14232
14235
|
if (!peer) {
|
|
14233
14236
|
return { kind: "drop", reason: "unknown_peer" };
|
|
14234
14237
|
}
|
|
14238
|
+
if (peer.gate_path === null) {
|
|
14239
|
+
return { kind: "drop", reason: "cross_team_grant_missing" };
|
|
14240
|
+
}
|
|
14241
|
+
if (cfg.peer_disabled_mode === "cross_team_only" && peer.gate_path !== void 0 && peer.gate_path !== "same_team") {
|
|
14242
|
+
return { kind: "drop", reason: "peer_disabled_cross_team" };
|
|
14243
|
+
}
|
|
14235
14244
|
if (self.bot_id === null || self.bot_username === null) {
|
|
14236
14245
|
return { kind: "drop", reason: "self_resolution_pending" };
|
|
14237
14246
|
}
|
|
@@ -14272,7 +14281,7 @@ function classifyAddressing(msg, ourBotId, ourBotUsername) {
|
|
|
14272
14281
|
return { addressed: false, viaReplyOnly: false };
|
|
14273
14282
|
}
|
|
14274
14283
|
var CODE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
14275
|
-
function parsePeersEnv(raw) {
|
|
14284
|
+
function parsePeersEnv(raw, gateRaw) {
|
|
14276
14285
|
if (!raw || raw.trim().length === 0) return [];
|
|
14277
14286
|
let parsed;
|
|
14278
14287
|
try {
|
|
@@ -14281,11 +14290,54 @@ function parsePeersEnv(raw) {
|
|
|
14281
14290
|
return [];
|
|
14282
14291
|
}
|
|
14283
14292
|
if (!Array.isArray(parsed)) return [];
|
|
14293
|
+
const gateMap = /* @__PURE__ */ new Map();
|
|
14294
|
+
let gateConfigInvalid = false;
|
|
14295
|
+
if (gateRaw && gateRaw.trim().length > 0) {
|
|
14296
|
+
try {
|
|
14297
|
+
const gateParsed = JSON.parse(gateRaw);
|
|
14298
|
+
if (!gateParsed || typeof gateParsed !== "object" || Array.isArray(gateParsed)) {
|
|
14299
|
+
gateConfigInvalid = true;
|
|
14300
|
+
} else {
|
|
14301
|
+
for (const [k, v] of Object.entries(gateParsed)) {
|
|
14302
|
+
if (v === null) {
|
|
14303
|
+
gateMap.set(k, null);
|
|
14304
|
+
} else if (typeof v === "string") {
|
|
14305
|
+
const isGrantPath = v.startsWith("grant:") && v.slice("grant:".length).trim().length > 0;
|
|
14306
|
+
if (v === "same_team" || v === "intra_org_unrestricted" || isGrantPath) {
|
|
14307
|
+
gateMap.set(k, v);
|
|
14308
|
+
} else {
|
|
14309
|
+
gateConfigInvalid = true;
|
|
14310
|
+
break;
|
|
14311
|
+
}
|
|
14312
|
+
} else {
|
|
14313
|
+
gateConfigInvalid = true;
|
|
14314
|
+
break;
|
|
14315
|
+
}
|
|
14316
|
+
}
|
|
14317
|
+
}
|
|
14318
|
+
} catch {
|
|
14319
|
+
gateConfigInvalid = true;
|
|
14320
|
+
}
|
|
14321
|
+
if (gateConfigInvalid) {
|
|
14322
|
+
console.error(
|
|
14323
|
+
"[telegram-peer-classifier] TELEGRAM_PEERS_GATE is present but malformed; failing closed (every peer drops as cross_team_grant_missing)"
|
|
14324
|
+
);
|
|
14325
|
+
}
|
|
14326
|
+
}
|
|
14284
14327
|
const out = [];
|
|
14285
14328
|
for (const entry of parsed) {
|
|
14286
14329
|
if (entry && typeof entry === "object" && typeof entry.code_name === "string" && CODE_NAME_RE.test(entry.code_name) && typeof entry.agent_id === "string" && typeof entry.bot_id === "number" && Number.isInteger(entry.bot_id) && entry.bot_id > 0) {
|
|
14287
14330
|
const e = entry;
|
|
14288
|
-
|
|
14331
|
+
const peer = { code_name: e.code_name, bot_id: e.bot_id, agent_id: e.agent_id };
|
|
14332
|
+
if (gateConfigInvalid) {
|
|
14333
|
+
peer.gate_path = null;
|
|
14334
|
+
} else {
|
|
14335
|
+
const key2 = String(e.bot_id);
|
|
14336
|
+
if (gateMap.has(key2)) {
|
|
14337
|
+
peer.gate_path = gateMap.get(key2) ?? null;
|
|
14338
|
+
}
|
|
14339
|
+
}
|
|
14340
|
+
out.push(peer);
|
|
14289
14341
|
}
|
|
14290
14342
|
}
|
|
14291
14343
|
return out;
|
|
@@ -14615,6 +14667,154 @@ function createDefaultPeerRateApiClient(args) {
|
|
|
14615
14667
|
};
|
|
14616
14668
|
}
|
|
14617
14669
|
|
|
14670
|
+
// src/cross-team-peer-audit-client.ts
|
|
14671
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
14672
|
+
function createCrossTeamPeerAuditClient(args) {
|
|
14673
|
+
if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
|
|
14674
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
14675
|
+
const log = args.log ?? (() => {
|
|
14676
|
+
});
|
|
14677
|
+
const base = args.agtHost.replace(/\/+$/, "");
|
|
14678
|
+
const agentId = args.agentId;
|
|
14679
|
+
const apiKey = args.agtApiKey;
|
|
14680
|
+
let cachedToken = null;
|
|
14681
|
+
let cachedTokenExpiresAt = 0;
|
|
14682
|
+
async function getToken() {
|
|
14683
|
+
if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
|
|
14684
|
+
const resp = await fetchImpl(`${base}/host/exchange`, {
|
|
14685
|
+
method: "POST",
|
|
14686
|
+
headers: { "Content-Type": "application/json" },
|
|
14687
|
+
body: JSON.stringify({ host_key: apiKey }),
|
|
14688
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
14689
|
+
});
|
|
14690
|
+
if (!resp.ok) {
|
|
14691
|
+
const body = await resp.text().catch(() => "");
|
|
14692
|
+
throw new Error(`/host/exchange failed (${resp.status}): ${body.slice(0, 200)}`);
|
|
14693
|
+
}
|
|
14694
|
+
const data = await resp.json();
|
|
14695
|
+
cachedToken = data.token;
|
|
14696
|
+
cachedTokenExpiresAt = data.expires_at ? new Date(data.expires_at).getTime() - 12e4 : Date.now() + 55 * 6e4;
|
|
14697
|
+
return cachedToken;
|
|
14698
|
+
}
|
|
14699
|
+
async function postOnce(event) {
|
|
14700
|
+
const token = await getToken();
|
|
14701
|
+
return fetchImpl(`${base}/host/cross-team-peer-event`, {
|
|
14702
|
+
method: "POST",
|
|
14703
|
+
headers: {
|
|
14704
|
+
Authorization: `Bearer ${token}`,
|
|
14705
|
+
"Content-Type": "application/json"
|
|
14706
|
+
},
|
|
14707
|
+
body: JSON.stringify({ agent_id: agentId, ...event }),
|
|
14708
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
14709
|
+
});
|
|
14710
|
+
}
|
|
14711
|
+
async function emitAsync(event) {
|
|
14712
|
+
try {
|
|
14713
|
+
let resp = await postOnce(event);
|
|
14714
|
+
if (resp.status === 401) {
|
|
14715
|
+
cachedToken = null;
|
|
14716
|
+
cachedTokenExpiresAt = 0;
|
|
14717
|
+
resp = await postOnce(event);
|
|
14718
|
+
}
|
|
14719
|
+
if (!resp.ok) {
|
|
14720
|
+
const body = await resp.text().catch(() => "");
|
|
14721
|
+
log(
|
|
14722
|
+
`cross-team-peer-audit: POST failed (${resp.status}) event=${event.event_type}: ${body.slice(0, 200)}`
|
|
14723
|
+
);
|
|
14724
|
+
}
|
|
14725
|
+
} catch (err) {
|
|
14726
|
+
log(`cross-team-peer-audit: emit threw event=${event.event_type}: ${err.message}`);
|
|
14727
|
+
}
|
|
14728
|
+
}
|
|
14729
|
+
return {
|
|
14730
|
+
emit(event) {
|
|
14731
|
+
void emitAsync(event);
|
|
14732
|
+
}
|
|
14733
|
+
};
|
|
14734
|
+
}
|
|
14735
|
+
|
|
14736
|
+
// src/observed-chat-client.ts
|
|
14737
|
+
var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
14738
|
+
function createObservedChatClient(args) {
|
|
14739
|
+
if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
|
|
14740
|
+
const fetchImpl = args.fetchImpl ?? fetch;
|
|
14741
|
+
const log = args.log ?? (() => {
|
|
14742
|
+
});
|
|
14743
|
+
const base = args.agtHost.replace(/\/+$/, "");
|
|
14744
|
+
const agentId = args.agentId;
|
|
14745
|
+
const apiKey = args.agtApiKey;
|
|
14746
|
+
const reported = /* @__PURE__ */ new Map();
|
|
14747
|
+
let cachedToken = null;
|
|
14748
|
+
let cachedTokenExpiresAt = 0;
|
|
14749
|
+
async function getToken() {
|
|
14750
|
+
if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
|
|
14751
|
+
const resp = await fetchImpl(`${base}/host/exchange`, {
|
|
14752
|
+
method: "POST",
|
|
14753
|
+
headers: { "Content-Type": "application/json" },
|
|
14754
|
+
body: JSON.stringify({ host_key: apiKey }),
|
|
14755
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
14756
|
+
});
|
|
14757
|
+
if (!resp.ok) {
|
|
14758
|
+
const body = await resp.text().catch(() => "");
|
|
14759
|
+
throw new Error(`/host/exchange failed (${resp.status}): ${body.slice(0, 200)}`);
|
|
14760
|
+
}
|
|
14761
|
+
const data = await resp.json();
|
|
14762
|
+
cachedToken = data.token;
|
|
14763
|
+
cachedTokenExpiresAt = data.expires_at ? new Date(data.expires_at).getTime() - 12e4 : Date.now() + 55 * 6e4;
|
|
14764
|
+
return cachedToken;
|
|
14765
|
+
}
|
|
14766
|
+
async function postOnce(chat) {
|
|
14767
|
+
const token = await getToken();
|
|
14768
|
+
return fetchImpl(`${base}/host/observed-chat`, {
|
|
14769
|
+
method: "POST",
|
|
14770
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
14771
|
+
body: JSON.stringify({ agent_id: agentId, ...chat }),
|
|
14772
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
14773
|
+
});
|
|
14774
|
+
}
|
|
14775
|
+
async function emitAsync(chat) {
|
|
14776
|
+
try {
|
|
14777
|
+
let resp = await postOnce(chat);
|
|
14778
|
+
if (resp.status === 401) {
|
|
14779
|
+
cachedToken = null;
|
|
14780
|
+
cachedTokenExpiresAt = 0;
|
|
14781
|
+
resp = await postOnce(chat);
|
|
14782
|
+
}
|
|
14783
|
+
if (!resp.ok) {
|
|
14784
|
+
const body = await resp.text().catch(() => "");
|
|
14785
|
+
log(
|
|
14786
|
+
`observed-chat: POST failed (${resp.status}) chat=${chat.chat_id}: ${body.slice(0, 200)}`
|
|
14787
|
+
);
|
|
14788
|
+
return false;
|
|
14789
|
+
}
|
|
14790
|
+
return true;
|
|
14791
|
+
} catch (err) {
|
|
14792
|
+
log(`observed-chat: emit threw chat=${chat.chat_id}: ${err.message}`);
|
|
14793
|
+
return false;
|
|
14794
|
+
}
|
|
14795
|
+
}
|
|
14796
|
+
return {
|
|
14797
|
+
observe(chat) {
|
|
14798
|
+
const prev = reported.get(chat.chat_id);
|
|
14799
|
+
if (prev && prev.title === (chat.chat_title ?? void 0) && prev.type === (chat.chat_type ?? void 0)) {
|
|
14800
|
+
return;
|
|
14801
|
+
}
|
|
14802
|
+
const snapshot = {
|
|
14803
|
+
title: chat.chat_title ?? void 0,
|
|
14804
|
+
type: chat.chat_type ?? void 0
|
|
14805
|
+
};
|
|
14806
|
+
reported.set(chat.chat_id, snapshot);
|
|
14807
|
+
void emitAsync(chat).then((ok) => {
|
|
14808
|
+
if (ok) return;
|
|
14809
|
+
const current = reported.get(chat.chat_id);
|
|
14810
|
+
if (current?.title === snapshot.title && current?.type === snapshot.type) {
|
|
14811
|
+
reported.delete(chat.chat_id);
|
|
14812
|
+
}
|
|
14813
|
+
});
|
|
14814
|
+
}
|
|
14815
|
+
};
|
|
14816
|
+
}
|
|
14817
|
+
|
|
14618
14818
|
// src/telegram-channel.ts
|
|
14619
14819
|
function redactId(id) {
|
|
14620
14820
|
return createHash("sha256").update(String(id)).digest("hex").slice(0, 8);
|
|
@@ -14629,18 +14829,45 @@ var ALLOWED_CHATS = new Set(
|
|
|
14629
14829
|
var PEER_CLASSIFIER_CONFIG = {
|
|
14630
14830
|
peer_agent_mode: parsePeerAgentModeEnv(process.env.TELEGRAM_PEER_AGENT_MODE),
|
|
14631
14831
|
peer_group_ids: parsePeerGroupIdsEnv(process.env.TELEGRAM_PEER_GROUP_IDS),
|
|
14632
|
-
peers: parsePeersEnv(process.env.TELEGRAM_PEERS)
|
|
14832
|
+
peers: parsePeersEnv(process.env.TELEGRAM_PEERS, process.env.TELEGRAM_PEERS_GATE),
|
|
14833
|
+
peer_disabled_mode: "off"
|
|
14834
|
+
// populated below from PEER_DISABLED_MODE
|
|
14633
14835
|
};
|
|
14634
|
-
var
|
|
14635
|
-
const
|
|
14636
|
-
|
|
14836
|
+
var PEER_DISABLED_MODE = (() => {
|
|
14837
|
+
const next = (process.env.PEER_DISABLED ?? "").trim().toLowerCase();
|
|
14838
|
+
if (next === "off" || next === "cross_team_only" || next === "all") return next;
|
|
14839
|
+
if (next.length > 0) {
|
|
14840
|
+
process.stderr.write(
|
|
14841
|
+
`telegram-channel(${AGENT_CODE_NAME}): invalid PEER_DISABLED=${JSON.stringify(process.env.PEER_DISABLED)} \u2014 defaulting to 'all' (fail-closed)
|
|
14842
|
+
`
|
|
14843
|
+
);
|
|
14844
|
+
return "all";
|
|
14845
|
+
}
|
|
14846
|
+
const legacy = (process.env.TELEGRAM_PEER_DISABLED ?? "").trim().toLowerCase();
|
|
14847
|
+
return legacy === "1" || legacy === "true" || legacy === "yes" ? "all" : "off";
|
|
14637
14848
|
})();
|
|
14638
|
-
|
|
14849
|
+
var PEER_DISABLED_GLOBAL = PEER_DISABLED_MODE === "all";
|
|
14850
|
+
if (PEER_DISABLED_MODE !== "off") {
|
|
14639
14851
|
process.stderr.write(
|
|
14640
|
-
`telegram-channel(${AGENT_CODE_NAME}):
|
|
14852
|
+
`telegram-channel(${AGENT_CODE_NAME}): PEER_DISABLED=${PEER_DISABLED_MODE} \u2014 ${PEER_DISABLED_MODE === "all" ? "every peer surface short-circuited" : "cross-team peers dropped, same-team peers admitted"}
|
|
14641
14853
|
`
|
|
14642
14854
|
);
|
|
14643
14855
|
}
|
|
14856
|
+
PEER_CLASSIFIER_CONFIG.peer_disabled_mode = PEER_DISABLED_MODE;
|
|
14857
|
+
var crossTeamPeerAuditClient = createCrossTeamPeerAuditClient({
|
|
14858
|
+
agtHost: AGT_HOST,
|
|
14859
|
+
agtApiKey: AGT_API_KEY,
|
|
14860
|
+
agentId: process.env.AGT_AGENT_ID ?? null,
|
|
14861
|
+
log: (line) => process.stderr.write(`telegram-channel(${AGENT_CODE_NAME}): ${line}
|
|
14862
|
+
`)
|
|
14863
|
+
});
|
|
14864
|
+
var observedChatClient = createObservedChatClient({
|
|
14865
|
+
agtHost: AGT_HOST,
|
|
14866
|
+
agtApiKey: AGT_API_KEY,
|
|
14867
|
+
agentId: process.env.AGT_AGENT_ID ?? null,
|
|
14868
|
+
log: (line) => process.stderr.write(`telegram-channel(${AGENT_CODE_NAME}): ${line}
|
|
14869
|
+
`)
|
|
14870
|
+
});
|
|
14644
14871
|
var peerRateApiClient = createDefaultPeerRateApiClient({
|
|
14645
14872
|
agtHost: AGT_HOST,
|
|
14646
14873
|
agtApiKey: AGT_API_KEY,
|
|
@@ -15588,6 +15815,11 @@ async function pollLoop() {
|
|
|
15588
15815
|
if (content.length === 0 && classifiedAttachments.length === 0) continue;
|
|
15589
15816
|
const chatId = String(msg.chat.id);
|
|
15590
15817
|
if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chatId)) continue;
|
|
15818
|
+
observedChatClient?.observe({
|
|
15819
|
+
chat_id: chatId,
|
|
15820
|
+
chat_title: msg.chat.title ?? msg.chat.username ?? msg.chat.first_name ?? null,
|
|
15821
|
+
chat_type: msg.chat.type ?? null
|
|
15822
|
+
});
|
|
15591
15823
|
const trimmedContent = content.trim();
|
|
15592
15824
|
if (isHelpSyntax(trimmedContent)) {
|
|
15593
15825
|
const disposition = await classifyRestartCommand(trimmedContent.replace(/^\/help/, "/restart"));
|
|
@@ -15669,6 +15901,16 @@ async function pollLoop() {
|
|
|
15669
15901
|
`telegram-channel(${AGENT_CODE_NAME}): peer_drop reason=${classification.reason} chat=${redactId(chatId)} from=${redactId(String(msg.from?.id ?? "unknown"))}
|
|
15670
15902
|
`
|
|
15671
15903
|
);
|
|
15904
|
+
if (classification.reason === "cross_team_grant_missing") {
|
|
15905
|
+
crossTeamPeerAuditClient?.emit({
|
|
15906
|
+
event_type: "telegram.peer.drop.cross_team_grant_missing",
|
|
15907
|
+
peer_agent_id: null,
|
|
15908
|
+
gate_path: null,
|
|
15909
|
+
grant_id: null,
|
|
15910
|
+
chat_id: String(chatId),
|
|
15911
|
+
message_id: String(msg.message_id)
|
|
15912
|
+
});
|
|
15913
|
+
}
|
|
15672
15914
|
continue;
|
|
15673
15915
|
}
|
|
15674
15916
|
if (classification.kind === "peer-ingress") {
|
|
@@ -15695,6 +15937,26 @@ async function pollLoop() {
|
|
|
15695
15937
|
code_name: classification.peer.code_name,
|
|
15696
15938
|
agent_id: classification.peer.agent_id
|
|
15697
15939
|
};
|
|
15940
|
+
const gate = classification.peer.gate_path;
|
|
15941
|
+
if (gate && gate !== "same_team" && gate !== "intra_org_unrestricted" && gate.startsWith("grant:")) {
|
|
15942
|
+
crossTeamPeerAuditClient?.emit({
|
|
15943
|
+
event_type: "telegram.peer.ingress.cross_team",
|
|
15944
|
+
peer_agent_id: classification.peer.agent_id || null,
|
|
15945
|
+
gate_path: gate,
|
|
15946
|
+
grant_id: gate.slice("grant:".length),
|
|
15947
|
+
chat_id: String(chatId),
|
|
15948
|
+
message_id: String(msg.message_id)
|
|
15949
|
+
});
|
|
15950
|
+
} else if (gate === "intra_org_unrestricted") {
|
|
15951
|
+
crossTeamPeerAuditClient?.emit({
|
|
15952
|
+
event_type: "telegram.peer.ingress.cross_team",
|
|
15953
|
+
peer_agent_id: classification.peer.agent_id || null,
|
|
15954
|
+
gate_path: gate,
|
|
15955
|
+
grant_id: null,
|
|
15956
|
+
chat_id: String(chatId),
|
|
15957
|
+
message_id: String(msg.message_id)
|
|
15958
|
+
});
|
|
15959
|
+
}
|
|
15698
15960
|
}
|
|
15699
15961
|
}
|
|
15700
15962
|
const messageId = String(msg.message_id);
|