@rubytech/create-realagent-code 0.1.612 → 0.1.613

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.612",
3
+ "version": "0.1.613",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Real Agent ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:5e127a3b4f988e5c2a96441ca87d017d72efde5c773223579f54ec14b4ea6fd6
4
+ content-hash: sha256:0f6a37637cbc5d7fc6fa7c444898e9609c467c5081c54bb27dcf1b979ffc7c36
5
5
  brand: realagent-code
6
6
  product-name: Real Agent
7
7
  ---
@@ -2961,10 +2961,10 @@ Telegram profile name and will tell you plainly that it cannot put a name to the
2961
2961
  Ask Real Agent to save the Telegram ID against the contact. It is one change to one contact, and
2962
2962
  it takes effect on the very next message.
2963
2963
 
2964
- ## Correcting what a public agent told a visitor
2964
+ ## Correcting what an agent told someone
2965
2965
 
2966
- When one of your public bots tells a visitor something wrong, the fix is to instruct the agent that
2967
- is talking to them, not to message the visitor yourself.
2966
+ When one of your bots tells someone something wrong, the fix is to instruct the agent that is talking
2967
+ to them, not to message that person yourself.
2968
2968
 
2969
2969
  The reason is worth understanding, because the obvious approach quietly fails. Every conversation is
2970
2970
  its own separate agent with its own memory. If you send the visitor a correction from your chat, they
@@ -2979,16 +2979,28 @@ own conversation. The agent writes the message itself, in the same voice it has
2979
2979
  own memory now holds what it said, so it will not contradict it a moment later. The visitor sees a
2980
2980
  normal message from the assistant they have been talking to, with no sign that anybody stepped in.
2981
2981
 
2982
+ The same is true of your specialist bots. If a specialist relays for a driver, a supplier or a
2983
+ colleague, and you card that person yourself, the specialist never learns you did. It answers their
2984
+ next message with no idea what they are replying to. That happened on a live conversation: a driver
2985
+ was sent a job by card, and when he came back twelve minutes later with his prices, the bot filed
2986
+ them as a passing remark and thanked him. Instruct it the same way, naming its bot and their chat,
2987
+ and it writes the message and remembers writing it.
2988
+
2982
2989
  Write the intent rather than the exact sentence. This is an instruction to the agent, not text
2983
2990
  delivered word for word, so "tell them we can do Thursday morning but not the afternoon" works better
2984
2991
  than a script.
2985
2992
 
2986
- Four situations are refused, and each one says why rather than failing quietly:
2987
-
2988
- - The bot is not a public bot. Your own admin bot and any specialist bot are conversations with your
2989
- own side, not with a visitor, so there is nothing to instruct.
2990
- - The agent has been switched off. Nothing is resurrected behind your back; turn it back on, or
2991
- message the person yourself.
2993
+ Six situations are refused, and each one says why rather than failing quietly:
2994
+
2995
+ - The bot is your own admin bot. That chat is your conversation with the agent, so instructing it
2996
+ there is the agent talking to itself.
2997
+ - The bot is bound to nothing. It answers nobody, so there is no agent to instruct.
2998
+ - The agent has been switched off, or a specialist's card has been renamed or deleted. Nothing is
2999
+ resurrected behind your back; turn it back on, or message the person yourself.
3000
+ - The bot is not allowed to talk to that person. Every bot has its own rule about who it answers, and
3001
+ a specialist answers a named list and nobody else. Starting a conversation the bot would refuse to
3002
+ continue leaves the person holding a message nobody can follow up, so it is refused instead. Most
3003
+ often this is a mistyped chat ID.
2992
3004
  - The chat belongs to a connected business account. Those conversations are held separately and this
2993
3005
  route cannot reach them.
2994
3006
  - The chat is a broadcast channel the bot does not allow. A channel post reaches every subscriber and
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-16 (0.1.613)
13
+
14
+ - You can now tell the assistant what to say to someone one of your specialist bots is handling, the same way you already could for a public bot. Until now that was refused, so the only way through was to message the person yourself, and the specialist never learned you had. It would answer their next message with no idea what they were replying to.
15
+ - A bot will no longer start a conversation with someone it is set up never to answer. A mistyped chat ID used to send one message and then go silent, because every reply came back to a bot that refused it.
16
+
12
17
  ## 2026-08-15 (0.1.612)
13
18
 
14
19
  - A conversation opened from the dashboard could show as completely empty even though the agent had answered in it. The reader was discarding the agent's replies instead of drawing them, so the thread looked like nothing had ever been said. Those replies now appear.
@@ -138,10 +138,10 @@ Telegram profile name and will tell you plainly that it cannot put a name to the
138
138
  Ask {{productName}} to save the Telegram ID against the contact. It is one change to one contact, and
139
139
  it takes effect on the very next message.
140
140
 
141
- ## Correcting what a public agent told a visitor
141
+ ## Correcting what an agent told someone
142
142
 
143
- When one of your public bots tells a visitor something wrong, the fix is to instruct the agent that
144
- is talking to them, not to message the visitor yourself.
143
+ When one of your bots tells someone something wrong, the fix is to instruct the agent that is talking
144
+ to them, not to message that person yourself.
145
145
 
146
146
  The reason is worth understanding, because the obvious approach quietly fails. Every conversation is
147
147
  its own separate agent with its own memory. If you send the visitor a correction from your chat, they
@@ -156,16 +156,28 @@ own conversation. The agent writes the message itself, in the same voice it has
156
156
  own memory now holds what it said, so it will not contradict it a moment later. The visitor sees a
157
157
  normal message from the assistant they have been talking to, with no sign that anybody stepped in.
158
158
 
159
+ The same is true of your specialist bots. If a specialist relays for a driver, a supplier or a
160
+ colleague, and you card that person yourself, the specialist never learns you did. It answers their
161
+ next message with no idea what they are replying to. That happened on a live conversation: a driver
162
+ was sent a job by card, and when he came back twelve minutes later with his prices, the bot filed
163
+ them as a passing remark and thanked him. Instruct it the same way, naming its bot and their chat,
164
+ and it writes the message and remembers writing it.
165
+
159
166
  Write the intent rather than the exact sentence. This is an instruction to the agent, not text
160
167
  delivered word for word, so "tell them we can do Thursday morning but not the afternoon" works better
161
168
  than a script.
162
169
 
163
- Four situations are refused, and each one says why rather than failing quietly:
164
-
165
- - The bot is not a public bot. Your own admin bot and any specialist bot are conversations with your
166
- own side, not with a visitor, so there is nothing to instruct.
167
- - The agent has been switched off. Nothing is resurrected behind your back; turn it back on, or
168
- message the person yourself.
170
+ Six situations are refused, and each one says why rather than failing quietly:
171
+
172
+ - The bot is your own admin bot. That chat is your conversation with the agent, so instructing it
173
+ there is the agent talking to itself.
174
+ - The bot is bound to nothing. It answers nobody, so there is no agent to instruct.
175
+ - The agent has been switched off, or a specialist's card has been renamed or deleted. Nothing is
176
+ resurrected behind your back; turn it back on, or message the person yourself.
177
+ - The bot is not allowed to talk to that person. Every bot has its own rule about who it answers, and
178
+ a specialist answers a named list and nobody else. Starting a conversation the bot would refuse to
179
+ continue leaves the person holding a message nobody can follow up, so it is refused instead. Most
180
+ often this is a mistyped chat ID.
169
181
  - The chat belongs to a connected business account. Those conversations are held separately and this
170
182
  route cannot reach them.
171
183
  - The chat is a broadcast channel the bot does not allow. A channel post reaches every subscriber and
@@ -22,8 +22,8 @@
22
22
  <link rel="modulepreload" crossorigin href="/assets/wrench-BI28h1JF.js">
23
23
  <link rel="modulepreload" crossorigin href="/assets/page-Bkl56P8R.js">
24
24
  <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-dao9WPN0.css">
25
- <link rel="stylesheet" crossorigin href="/assets/useMediaQuery-DXAuc8G-.css">
26
25
  <link rel="stylesheet" crossorigin href="/assets/AdminLoginScreens-CWMpccrR.css">
26
+ <link rel="stylesheet" crossorigin href="/assets/useMediaQuery-DXAuc8G-.css">
27
27
  <link rel="stylesheet" href="/brand-defaults.css">
28
28
  </head>
29
29
  <body>
@@ -6248,6 +6248,82 @@ function parseTelegramChannelKey(key2) {
6248
6248
  return { botId, senderId: rest, business: false };
6249
6249
  }
6250
6250
 
6251
+ // app/lib/telegram/access-control.ts
6252
+ function checkTelegramAccess(params) {
6253
+ const { senderId, entry: entry3, chatId, chatType } = params;
6254
+ const isGroup = chatType === "group" || chatType === "supergroup";
6255
+ const isChannel = chatType === "channel";
6256
+ if (isChannel && entry3.role !== "public") {
6257
+ return {
6258
+ allowed: false,
6259
+ reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
6260
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
6261
+ };
6262
+ }
6263
+ if (isGroup && entry3.role !== "public") {
6264
+ return {
6265
+ allowed: false,
6266
+ reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
6267
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
6268
+ };
6269
+ }
6270
+ if (entry3.role === "admin") {
6271
+ const adminUsers = entry3.adminUsers ?? [];
6272
+ if (adminUsers.includes(senderId)) {
6273
+ return { allowed: true, reason: "admin-binding", agentType: "admin" };
6274
+ }
6275
+ return { allowed: false, reason: "not-admin-user", agentType: "admin" };
6276
+ }
6277
+ if (isGroup && entry3.role === "public") {
6278
+ const groupPolicy = entry3.groupPolicy ?? "disabled";
6279
+ if (groupPolicy === "allowlist") {
6280
+ const allowGroups = entry3.allowGroups ?? [];
6281
+ if (allowGroups.includes(chatId)) {
6282
+ return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
6283
+ }
6284
+ return { allowed: false, reason: "not-in-allowGroups", agentType: "public" };
6285
+ }
6286
+ return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
6287
+ }
6288
+ if (isChannel && entry3.role === "public") {
6289
+ const channelPolicy = entry3.channelPolicy ?? "disabled";
6290
+ if (channelPolicy === "allowlist") {
6291
+ const allowChannels = entry3.allowChannels ?? [];
6292
+ if (allowChannels.includes(chatId)) {
6293
+ return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
6294
+ }
6295
+ return { allowed: false, reason: "not-in-allowChannels", agentType: "public" };
6296
+ }
6297
+ return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
6298
+ }
6299
+ if (entry3.role === "unbound") {
6300
+ return { allowed: false, reason: "bot-unbound", agentType: "public" };
6301
+ }
6302
+ if (entry3.role === "specialist") {
6303
+ const allowFrom = entry3.allowFrom ?? [];
6304
+ if (allowFrom.includes(senderId)) {
6305
+ return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
6306
+ }
6307
+ return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
6308
+ }
6309
+ const policy = entry3.dmPolicy ?? "disabled";
6310
+ switch (policy) {
6311
+ case "open":
6312
+ return { allowed: true, reason: "dm-policy-open", agentType: "public" };
6313
+ case "allowlist": {
6314
+ const allowFrom = entry3.allowFrom ?? [];
6315
+ if (allowFrom.includes(senderId)) {
6316
+ return { allowed: true, reason: "allowlist-match", agentType: "public" };
6317
+ }
6318
+ return { allowed: false, reason: "not-in-allowlist", agentType: "public" };
6319
+ }
6320
+ case "disabled":
6321
+ return { allowed: false, reason: "dm-policy-disabled", agentType: "public" };
6322
+ default:
6323
+ return { allowed: false, reason: "unknown-dm-policy", agentType: "public" };
6324
+ }
6325
+ }
6326
+
6251
6327
  // app/lib/telegram/chat-send-policy.ts
6252
6328
  var BROADCAST_PREFIX = "-100";
6253
6329
  function checkTelegramChatSend(params) {
@@ -6263,6 +6339,21 @@ function chatSendAllowlists(entry3) {
6263
6339
  if (!entry3 || entry3.role !== "public") return { allowChannels: [], allowGroups: [] };
6264
6340
  return { allowChannels: entry3.allowChannels ?? [], allowGroups: entry3.allowGroups ?? [] };
6265
6341
  }
6342
+ function checkTelegramOutboundTarget(params) {
6343
+ const { chatId, entry: entry3 } = params;
6344
+ if (String(chatId).startsWith(BROADCAST_PREFIX)) {
6345
+ const lists = chatSendAllowlists(entry3);
6346
+ const verdict2 = checkTelegramChatSend({
6347
+ chatId,
6348
+ allowChannels: lists.allowChannels,
6349
+ allowGroups: lists.allowGroups
6350
+ });
6351
+ return { allowed: verdict2.allowed, reason: verdict2.reason, kind: "broadcast" };
6352
+ }
6353
+ const kind = chatId < 0 ? "group" : "private";
6354
+ const verdict = checkTelegramAccess({ senderId: chatId, entry: entry3, chatId, chatType: kind });
6355
+ return { allowed: verdict.allowed, reason: verdict.reason, kind };
6356
+ }
6266
6357
 
6267
6358
  // app/lib/channel-pty-bridge/admin-session-id.ts
6268
6359
  import { createHash } from "crypto";
@@ -7732,13 +7823,24 @@ function createOperatorInjectRoutes(deps) {
7732
7823
  const { accountId, accountDir, entry: entry3 } = resolved;
7733
7824
  const role = entry3.role;
7734
7825
  const agent = agentOf(entry3);
7735
- if (role !== "public") {
7736
- report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "not-public-bot" });
7826
+ if (role === "admin") {
7827
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "admin-bot" });
7737
7828
  return c.json(
7738
7829
  {
7739
7830
  ok: false,
7740
- error: "not-public-bot",
7741
- detail: `Bot ${botId} is a ${role} bot, not a public one. Only a public agent's conversation with a visitor can be instructed this way.`
7831
+ error: "admin-bot",
7832
+ detail: `Bot ${botId} is this account's admin bot. Its chat is your own conversation with the agent, so there is nothing to instruct.`
7833
+ },
7834
+ 403
7835
+ );
7836
+ }
7837
+ if (role === "unbound") {
7838
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "unbound-bot" });
7839
+ return c.json(
7840
+ {
7841
+ ok: false,
7842
+ error: "unbound-bot",
7843
+ detail: `Bot ${botId} is not bound to an agent or a specialist card, so it answers nobody. Bind it before instructing it.`
7742
7844
  },
7743
7845
  403
7744
7846
  );
@@ -7771,30 +7873,18 @@ function createOperatorInjectRoutes(deps) {
7771
7873
  403
7772
7874
  );
7773
7875
  }
7774
- const lists = chatSendAllowlists(entry3);
7775
- const verdict = checkTelegramChatSend({
7776
- chatId: Number(chatId),
7777
- allowChannels: lists.allowChannels,
7778
- allowGroups: lists.allowGroups
7779
- });
7876
+ const verdict = checkTelegramOutboundTarget({ chatId: Number(chatId), entry: entry3 });
7780
7877
  console.error(
7781
7878
  `[telegram-outbound] op=channel-send botId=${logValue2(botId)} chatId=${logValue2(chatId)} allowed=${verdict.allowed ? "yes" : "no"} reason=${verdict.reason} messageId=none`
7782
7879
  );
7783
7880
  if (!verdict.allowed) {
7784
- report({
7785
- botId,
7786
- chatId,
7787
- sessionId: "none",
7788
- role,
7789
- agent,
7790
- accepted: false,
7791
- reason: "chat-not-allowlisted"
7792
- });
7881
+ const outcome = verdict.kind === "broadcast" ? "chat-not-allowlisted" : "chat-not-permitted";
7882
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: outcome });
7793
7883
  return c.json(
7794
7884
  {
7795
7885
  ok: false,
7796
- error: "chat-not-allowlisted",
7797
- detail: `Chat ${chatId} is a broadcast channel that bot ${botId} does not allowlist, so nothing was sent.`
7886
+ error: outcome,
7887
+ detail: verdict.kind === "broadcast" ? `Chat ${chatId} is a broadcast channel that bot ${botId} does not allowlist, so nothing was sent.` : `Bot ${botId} may not talk to chat ${chatId} (${verdict.reason}), so nothing was sent. It would refuse that chat's own messages too.`
7798
7888
  },
7799
7889
  403
7800
7890
  );
@@ -7823,13 +7913,22 @@ function createOperatorInjectRoutes(deps) {
7823
7913
  accountDir,
7824
7914
  botId,
7825
7915
  botToken: entry3.token,
7826
- agentSlug: entry3.agent,
7916
+ // Task 2773 — `entryAgentSlug`, not `entry.agent`: a specialist entry
7917
+ // carries a card name and no `agent` field at all, so the property read
7918
+ // is `undefined` there. The webhook resolves the same way
7919
+ // (`routes/telegram.ts`), so both doors name the spawn's agent from one
7920
+ // definition.
7921
+ agentSlug: entryAgentSlug(entry3),
7827
7922
  senderId: chatId,
7828
7923
  conversationSubject: chatId,
7829
7924
  businessChatId: null,
7830
- role: "public",
7925
+ // Task 2773 — the entry's OWN role, replacing a hardcoded 'public'. This
7926
+ // picks the session derivation in `sessionIdFor` and the framing in the
7927
+ // channel server; a value disagreeing with the registry would land the
7928
+ // instruction in a session the counterparty's real messages never reach.
7929
+ role,
7831
7930
  personId: null,
7832
- specialist: null,
7931
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
7833
7932
  chatId,
7834
7933
  text: instruction,
7835
7934
  // An instruction carries no file by construction: there is no message to
@@ -15516,11 +15615,19 @@ async function recordManualSendContext(input, deps) {
15516
15615
  mechanism,
15517
15616
  reason
15518
15617
  });
15519
- if (input.entry.role !== "public") return refuse2("not-public-bot");
15618
+ if (input.entry.role === "admin") return refuse2("admin-bot");
15619
+ if (input.entry.role === "unbound") return refuse2("unbound-bot");
15520
15620
  const businessKey = telegramBusinessChannelKey(input.botId, String(input.chatId));
15521
15621
  if (deps.newestInboundTelegramDate(input.accountId, businessKey) !== null) {
15522
15622
  return refuse2("business-chat");
15523
15623
  }
15624
+ const target = checkTelegramOutboundTarget({ chatId: input.chatId, entry: input.entry });
15625
+ if (!target.allowed) {
15626
+ deps.log(
15627
+ `[telegram-manual-send] op=target-refused botId=${input.botId} chatId=${input.chatId} kind=${target.kind} reason=${target.reason}`
15628
+ );
15629
+ return refuse2("chat-not-permitted");
15630
+ }
15524
15631
  const projectDir = deps.findSessionProjectDir(input.sessionId);
15525
15632
  if (projectDir === null) return refuse2("no-transcript");
15526
15633
  const live = deps.isLive(input.channelKey);
@@ -15541,16 +15648,22 @@ async function recordManualSendContext(input, deps) {
15541
15648
  accountDir: input.accountDir,
15542
15649
  botId: input.botId,
15543
15650
  botToken: input.entry.token,
15544
- agentSlug: input.entry.agent,
15651
+ // Task 2773 — `entryAgentSlug`, not `entry.agent`: a specialist entry has
15652
+ // no `agent` field, so the property read is `undefined` there.
15653
+ agentSlug: entryAgentSlug(input.entry),
15545
15654
  senderId: input.senderId,
15546
15655
  // The route admits only a private chat, where the conversation's subject IS
15547
15656
  // the sender. Passed explicitly so the gateway's key and its session id are
15548
15657
  // derived from the same value the store row already carries.
15549
15658
  conversationSubject: input.senderId,
15550
15659
  businessChatId: null,
15551
- role: "public",
15660
+ // Task 2773 — the entry's OWN role, replacing a hardcoded 'public'. It
15661
+ // picks the session derivation in `sessionIdFor`, which for a specialist is
15662
+ // `telegramAdminSessionId` — the same value the caller already derived for
15663
+ // `input.sessionId`.
15664
+ role: input.entry.role,
15552
15665
  personId: null,
15553
- specialist: null,
15666
+ specialist: input.entry.role === "specialist" ? input.entry.specialist : null,
15554
15667
  chatId: String(input.chatId),
15555
15668
  text: input.text,
15556
15669
  // No file by construction: this is text the operator typed into the send
@@ -18842,82 +18955,6 @@ async function setManagedBotAccessSettings(managingBotToken, userId, isAccessRes
18842
18955
  return r.ok ? { ok: true } : { ok: false, error: r.error };
18843
18956
  }
18844
18957
 
18845
- // app/lib/telegram/access-control.ts
18846
- function checkTelegramAccess(params) {
18847
- const { senderId, entry: entry3, chatId, chatType } = params;
18848
- const isGroup = chatType === "group" || chatType === "supergroup";
18849
- const isChannel = chatType === "channel";
18850
- if (isChannel && entry3.role !== "public") {
18851
- return {
18852
- allowed: false,
18853
- reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
18854
- agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18855
- };
18856
- }
18857
- if (isGroup && entry3.role !== "public") {
18858
- return {
18859
- allowed: false,
18860
- reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
18861
- agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18862
- };
18863
- }
18864
- if (entry3.role === "admin") {
18865
- const adminUsers = entry3.adminUsers ?? [];
18866
- if (adminUsers.includes(senderId)) {
18867
- return { allowed: true, reason: "admin-binding", agentType: "admin" };
18868
- }
18869
- return { allowed: false, reason: "not-admin-user", agentType: "admin" };
18870
- }
18871
- if (isGroup && entry3.role === "public") {
18872
- const groupPolicy = entry3.groupPolicy ?? "disabled";
18873
- if (groupPolicy === "allowlist") {
18874
- const allowGroups = entry3.allowGroups ?? [];
18875
- if (allowGroups.includes(chatId)) {
18876
- return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
18877
- }
18878
- return { allowed: false, reason: "not-in-allowGroups", agentType: "public" };
18879
- }
18880
- return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
18881
- }
18882
- if (isChannel && entry3.role === "public") {
18883
- const channelPolicy = entry3.channelPolicy ?? "disabled";
18884
- if (channelPolicy === "allowlist") {
18885
- const allowChannels = entry3.allowChannels ?? [];
18886
- if (allowChannels.includes(chatId)) {
18887
- return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
18888
- }
18889
- return { allowed: false, reason: "not-in-allowChannels", agentType: "public" };
18890
- }
18891
- return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
18892
- }
18893
- if (entry3.role === "unbound") {
18894
- return { allowed: false, reason: "bot-unbound", agentType: "public" };
18895
- }
18896
- if (entry3.role === "specialist") {
18897
- const allowFrom = entry3.allowFrom ?? [];
18898
- if (allowFrom.includes(senderId)) {
18899
- return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
18900
- }
18901
- return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
18902
- }
18903
- const policy = entry3.dmPolicy ?? "disabled";
18904
- switch (policy) {
18905
- case "open":
18906
- return { allowed: true, reason: "dm-policy-open", agentType: "public" };
18907
- case "allowlist": {
18908
- const allowFrom = entry3.allowFrom ?? [];
18909
- if (allowFrom.includes(senderId)) {
18910
- return { allowed: true, reason: "allowlist-match", agentType: "public" };
18911
- }
18912
- return { allowed: false, reason: "not-in-allowlist", agentType: "public" };
18913
- }
18914
- case "disabled":
18915
- return { allowed: false, reason: "dm-policy-disabled", agentType: "public" };
18916
- default:
18917
- return { allowed: false, reason: "unknown-dm-policy", agentType: "public" };
18918
- }
18919
- }
18920
-
18921
18958
  // app/lib/telegram/managed-bot.ts
18922
18959
  function parseManagedBotUpdate(u) {
18923
18960
  const id = u?.bot?.id;