@integrity-labs/agt-cli 0.28.974 → 0.28.976
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 +5 -5
- package/dist/{chunk-3PYNSD4I.js → chunk-CIC25QXS.js} +14 -14
- package/dist/{chunk-3PYNSD4I.js.map → chunk-CIC25QXS.js.map} +1 -1
- package/dist/{chunk-PHPOHMSK.js → chunk-KTFN2AIB.js} +2 -2
- package/dist/{chunk-HRHDATT5.js → chunk-VF5BT7BP.js} +42 -21
- package/dist/{chunk-HRHDATT5.js.map → chunk-VF5BT7BP.js.map} +1 -1
- package/dist/{claude-pair-runtime-U6GBR7KM.js → claude-pair-runtime-XOIAF3ZU.js} +2 -2
- package/dist/lib/manager-worker.js +14 -14
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +71 -24
- package/dist/mcp/index.js +38 -17
- package/dist/mcp/origami.js +34 -13
- package/dist/mcp/slack-channel.js +56 -35
- package/dist/mcp/teams-channel.js +5 -5
- package/dist/mcp/telegram-channel.js +49 -28
- package/dist/mcp/whatsapp-channel.js +9 -9
- package/dist/{persistent-session-YJJXZSUE.js → persistent-session-26W47NVS.js} +3 -3
- package/dist/{responsiveness-probe-SQZGSNVI.js → responsiveness-probe-KMHYGGYP.js} +3 -3
- package/dist/{session-auth-dead-7OLNDHJC.js → session-auth-dead-CRG2ESD2.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-PHPOHMSK.js.map → chunk-KTFN2AIB.js.map} +0 -0
- /package/dist/{claude-pair-runtime-U6GBR7KM.js.map → claude-pair-runtime-XOIAF3ZU.js.map} +0 -0
- /package/dist/{persistent-session-YJJXZSUE.js.map → persistent-session-26W47NVS.js.map} +0 -0
- /package/dist/{responsiveness-probe-SQZGSNVI.js.map → responsiveness-probe-KMHYGGYP.js.map} +0 -0
- /package/dist/{session-auth-dead-7OLNDHJC.js.map → session-auth-dead-CRG2ESD2.js.map} +0 -0
|
@@ -39177,11 +39177,18 @@ var FLAG_REGISTRY = [
|
|
|
39177
39177
|
},
|
|
39178
39178
|
{
|
|
39179
39179
|
key: "ip-allowlist-enforce-admin-debug",
|
|
39180
|
-
description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture.
|
|
39180
|
+
description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. There is deliberately NO counterpart flag for the per-agent MCP surface: that one is shadow-only structurally (its ENFORCE_FLAG entry is null), so arming this one does not and cannot arm that one. See the comment below this entry for why.",
|
|
39181
39181
|
flagType: "boolean",
|
|
39182
|
-
// Shadow. The measurement comes first
|
|
39183
|
-
//
|
|
39184
|
-
//
|
|
39182
|
+
// Shadow. The measurement comes first - and ENG-10664 corrected what the
|
|
39183
|
+
// measurement is OF. This comment used to say "nobody yet knows the real set
|
|
39184
|
+
// of staff addresses": there is no such set. Measured over 48h, all 315
|
|
39185
|
+
// requests to this surface came from ONE address, the agt-hosts AZ-2a NAT
|
|
39186
|
+
// EIP - an AGENT calling the debug_* tools, not a person. The decided policy
|
|
39187
|
+
// is that it stays that way (admin-debug is only ever reached from our own
|
|
39188
|
+
// NAT gateways). So the pre-arm check is not "does my laptop appear" but
|
|
39189
|
+
// "is every NAT EIP in the list", because an AZ whose NAT has not yet
|
|
39190
|
+
// carried a debug_* call is absent from the log and would be refused the
|
|
39191
|
+
// instant this flips. See docs/runbooks/ip-allowlist-rollout.md step 3.
|
|
39185
39192
|
defaultValue: false,
|
|
39186
39193
|
// Arming it changes who can reach production diagnostics. Worth one
|
|
39187
39194
|
// confirmation.
|
|
@@ -39196,20 +39203,34 @@ var FLAG_REGISTRY = [
|
|
|
39196
39203
|
// ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
|
|
39197
39204
|
// flag is consulted and no flag flip can arm it.
|
|
39198
39205
|
//
|
|
39199
|
-
// Why it is not simply a second flag defaulting false
|
|
39200
|
-
//
|
|
39201
|
-
//
|
|
39202
|
-
//
|
|
39203
|
-
//
|
|
39204
|
-
//
|
|
39205
|
-
//
|
|
39206
|
+
// Why it is not simply a second flag defaulting false (CORRECTED, ENG-10664):
|
|
39207
|
+
//
|
|
39208
|
+
// This comment used to say the callers are agent HOSTS, most of which have no
|
|
39209
|
+
// allowlistable address per ADR-0032. That is the wrong caller. Agent hosts do
|
|
39210
|
+
// not call this surface at all: its handler's first act is
|
|
39211
|
+
// validateMcpAccessToken() against `mcp_oauth_tokens` (routes/mcp.ts:87, token
|
|
39212
|
+
// check at :91), so a host JWT is rejected outright - routes/host-runtime.ts
|
|
39213
|
+
// :33421 says so in as many words, which is why the ENG-4724 /host/* twins
|
|
39214
|
+
// exist. The caller set is remote MCP OAuth clients, structurally.
|
|
39215
|
+
//
|
|
39216
|
+
// The conclusion is unchanged and ONLY the reason has moved. Allowlisting this
|
|
39217
|
+
// surface would mean pinning a THIRD PARTY's egress ranges rather than our own
|
|
39218
|
+
// NAT EIPs - a different control, a different owner, a different failure mode:
|
|
39219
|
+
// they move a range and agent MCP access breaks with no signal on our side.
|
|
39220
|
+
// Per docs/runbooks/composio-allowlist-rollout.md this class of change has no
|
|
39206
39221
|
// fleet-level undo. A flag would leave that one toggle away during an
|
|
39207
39222
|
// incident, from someone who had just armed the admin-debug surface and
|
|
39208
39223
|
// reasonably assumed the pair moved together. Requiring a code change puts
|
|
39209
39224
|
// review in front of an irreversible action, which is the right price.
|
|
39210
39225
|
//
|
|
39211
|
-
//
|
|
39212
|
-
//
|
|
39226
|
+
// Keeping the false rationale would have been worse than having none: a reader
|
|
39227
|
+
// who checked it would find it does not hold and might "fix" it by adding the
|
|
39228
|
+
// flag. That is the failure this rewrite exists to prevent.
|
|
39229
|
+
//
|
|
39230
|
+
// If we ever DECIDE to take on that third-party dependency deliberately,
|
|
39231
|
+
// adding the flag here and a key in ENFORCE_FLAG is a small diff - but it is a
|
|
39232
|
+
// decision to own someone else's egress, not a discovery that the population
|
|
39233
|
+
// was allowlistable all along.
|
|
39213
39234
|
];
|
|
39214
39235
|
var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
|
|
39215
39236
|
|
|
@@ -40436,7 +40457,7 @@ function formatThreadMessages(messages, nameById) {
|
|
|
40436
40457
|
}
|
|
40437
40458
|
function capThreadContext(formatted, maxChars) {
|
|
40438
40459
|
if (formatted.length <= maxChars) return formatted;
|
|
40439
|
-
const marker = "[\u2026earlier thread messages omitted \u2014
|
|
40460
|
+
const marker = "[\u2026earlier thread messages omitted \u2014 slack_read_thread for full history\u2026]\n";
|
|
40440
40461
|
if (maxChars <= marker.length) return marker.slice(0, maxChars);
|
|
40441
40462
|
const tailBudget = maxChars - marker.length;
|
|
40442
40463
|
const tail = formatted.slice(formatted.length - tailBudget);
|
|
@@ -43136,13 +43157,13 @@ function classifyByCoords(mode, args, registry2, now, unknownInboundId, knownThr
|
|
|
43136
43157
|
function buildSlackMcpInstructions(opts) {
|
|
43137
43158
|
const { ackReaction, skipReactionOn, skipReaction } = opts;
|
|
43138
43159
|
return [
|
|
43139
|
-
"CRITICAL: every response to a Slack <channel> tag MUST go through
|
|
43140
|
-
"Each inbound tag carries inbound_id and message_ts, and sometimes thread_context. Reply with
|
|
43141
|
-
"Attachments: <channel> `files` is a JSON string - JSON.parse it. Entry with `path` (or a top-level `image_path`) is already downloaded - Read it; do NOT call
|
|
43160
|
+
"CRITICAL: every response to a Slack <channel> tag MUST go through slack_reply - typed text never reaches the user. Slow work: interim ack (slack_reply interim:true), then your FINAL answer as a separate slack_reply (interim omitted). An ack is not the answer.",
|
|
43161
|
+
"Each inbound tag carries inbound_id and message_ts, and sometimes thread_context. Reply with slack_reply passing inbound_id (copy it verbatim from the tag you are answering) + text; the server routes it to the right thread. Multiple <channel> tags = separate conversations: NEVER answer one user in another's inbound_id/thread. thread_context = pre-loaded thread; ground replies ONLY in it. For a self-initiated message (not a reply) set proactive:true.",
|
|
43162
|
+
"Attachments: <channel> `files` is a JSON string - JSON.parse it. Entry with `path` (or a top-level `image_path`) is already downloaded - Read it; do NOT call slack_download_attachment. Only entries with `file_id` and no `path` (PDF/docx/csv) need slack_download_attachment (pass file_id + channel), then Read the returned path. Don't surface file-handling errors that don't affect the answer.",
|
|
43142
43163
|
"Address users by user_name, never raw user ID. In multi-participant threads the CURRENT speaker is on the latest <channel> tag.",
|
|
43143
|
-
`Reactions (
|
|
43144
|
-
skipReactionOn ? `Decided NOT to reply to a DM or thread message (different @-mention, side chat, auto_followed catch-up, nothing useful)?
|
|
43145
|
-
'auto_followed="true" \u2192 reply only if useful (self-mentions auto-cleared server-side). own_bot_user_id=your Slack user ID. File:
|
|
43164
|
+
`Reactions (slack_react sparingly - prefer a reply): :${ackReaction}: = ack (auto-added on inbound, don't duplicate); \u2705 = success.` + (skipReactionOn ? ` "${skipReaction}" = seen, not replying.` : "") + " NEVER react to signal failure of your work - on failure slack_reply one sentence explaining it (no stack traces/secrets). \u23F3 and \u274C are system-only; never add them.",
|
|
43165
|
+
skipReactionOn ? `Decided NOT to reply to a DM or thread message (different @-mention, side chat, auto_followed catch-up, nothing useful)? slack_react with "${skipReaction}" so the sender knows you saw it - never post "this wasn't for me". Top-level channel messages you may skip silently.` : "A thread message NOT addressed to you (different @-mention, side chat, auto_followed catch-up): SILENTLY SKIP - no reaction, no reply.",
|
|
43166
|
+
'auto_followed="true" \u2192 reply only if useful (self-mentions auto-cleared server-side). own_bot_user_id=your Slack user ID. File: slack_upload_file path+channel+inbound_id.'
|
|
43146
43167
|
].join(" ");
|
|
43147
43168
|
}
|
|
43148
43169
|
|
|
@@ -43150,7 +43171,7 @@ function buildSlackMcpInstructions(opts) {
|
|
|
43150
43171
|
function buildSlackReactToolDescription(opts) {
|
|
43151
43172
|
const { skipReactionOn, skipReaction } = opts;
|
|
43152
43173
|
const skip = skipReactionOn ? skipReaction.trim() : "";
|
|
43153
|
-
return "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. " + (skip ? `":${skip}:" = seen, deliberately NOT replying \u2014 use it on a DM or thread message you correctly chose not to answer (different @-mention, side chat, auto_followed catch-up, nothing useful to add). This one is not cosmetic: it CLOSES the pending-inbound marker, so the message stops being re-delivered to you and the reply-recovery net will not publish your end-of-turn text into a thread you stayed out of. Skipping silently leaves that marker open. (Top-level channel messages you may skip silently.) ` : "") + "\u{1F4DD} (:memo:) and \u26A0\uFE0F (:warning:) are blessed for explicit multi-state STATUS pipelines only \u2014 e.g. marking a run/parent \u2705 done / \u{1F4DD} waiting / \u26A0\uFE0F failed for at-a-glance status. That exception is narrow: never use a reaction to tell a user that YOUR reply failed \u2014 NEVER react to signal failure of your own work; on failure call
|
|
43174
|
+
return "Add an emoji reaction to a Slack message. Use sparingly \u2014 prefer a text reply. Reaction taxonomy: \u2705 = action completed successfully. " + (skip ? `":${skip}:" = seen, deliberately NOT replying \u2014 use it on a DM or thread message you correctly chose not to answer (different @-mention, side chat, auto_followed catch-up, nothing useful to add). This one is not cosmetic: it CLOSES the pending-inbound marker, so the message stops being re-delivered to you and the reply-recovery net will not publish your end-of-turn text into a thread you stayed out of. Skipping silently leaves that marker open. (Top-level channel messages you may skip silently.) ` : "") + "\u{1F4DD} (:memo:) and \u26A0\uFE0F (:warning:) are blessed for explicit multi-state STATUS pipelines only \u2014 e.g. marking a run/parent \u2705 done / \u{1F4DD} waiting / \u26A0\uFE0F failed for at-a-glance status. That exception is narrow: never use a reaction to tell a user that YOUR reply failed \u2014 NEVER react to signal failure of your own work; on failure call slack_reply with one sentence explaining what went wrong instead (no stack traces, no secrets). \u{1F440} (eyes) is already auto-applied on inbound; do not duplicate. \u23F3 (:hourglass_flowing_sand:) is reserved for the system to mark an inbound that arrived while the agent couldn't reply \u2014 never add \u23F3 or \u274C yourself. (A \u{1F512} you may see on a thread is the server's reply-throttle marker, not a coerced reaction \u2014 slack_react passes your emoji straight through to Slack and never rewrites it to \u{1F512}.)";
|
|
43154
43175
|
}
|
|
43155
43176
|
|
|
43156
43177
|
// src/slack-routed-to-note.ts
|
|
@@ -47366,7 +47387,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47366
47387
|
// whether the agent still owes a substantive answer.
|
|
47367
47388
|
interim: {
|
|
47368
47389
|
type: "boolean",
|
|
47369
|
-
description: 'Set true ONLY when this is an interim acknowledgement (e.g. "On it \u2014 checking now\u2026") and the real answer is still coming. The system keeps tracking this conversation as awaiting your final reply. Your eventual substantive answer MUST be a separate
|
|
47390
|
+
description: 'Set true ONLY when this is an interim acknowledgement (e.g. "On it \u2014 checking now\u2026") and the real answer is still coming. The system keeps tracking this conversation as awaiting your final reply. Your eventual substantive answer MUST be a separate slack_reply with interim omitted/false \u2014 that is what marks the request complete. Omit (default false) for any reply that fully answers the user.'
|
|
47370
47391
|
},
|
|
47371
47392
|
// WS2 (cross-thread reply routing): the preferred way to target a reply.
|
|
47372
47393
|
inbound_id: {
|
|
@@ -47404,14 +47425,14 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47404
47425
|
properties: {
|
|
47405
47426
|
channel: { type: "string", description: "Slack channel ID" },
|
|
47406
47427
|
timestamp: { type: "string", description: "Message timestamp to react to" },
|
|
47407
|
-
emoji: { type: "string", description: 'Emoji name without colons (e.g. "white_check_mark", "memo", "warning"). Do NOT use "x" (\u274C) or "hourglass_flowing_sand" (\u23F3) \u2014 those are system-only. To signal that your own reply failed, send a
|
|
47428
|
+
emoji: { type: "string", description: 'Emoji name without colons (e.g. "white_check_mark", "memo", "warning"). Do NOT use "x" (\u274C) or "hourglass_flowing_sand" (\u23F3) \u2014 those are system-only. To signal that your own reply failed, send a slack_reply explaining it rather than a reaction. See the tool description for the full taxonomy.' }
|
|
47408
47429
|
},
|
|
47409
47430
|
required: ["channel", "timestamp", "emoji"]
|
|
47410
47431
|
}
|
|
47411
47432
|
},
|
|
47412
47433
|
{
|
|
47413
47434
|
name: "slack.update",
|
|
47414
|
-
description: 'Edit the text of a Slack message YOU already sent (Slack chat.update). Pass the channel and the message\'s ts (
|
|
47435
|
+
description: 'Edit the text of a Slack message YOU already sent (Slack chat.update). Pass the channel and the message\'s ts (slack_reply returns it as `(ts=\u2026)` on a successful send) plus the new text. You can only edit your OWN messages. This REPLACES the message text - any block layout on the original is cleared - so use it to correct a typo or update a message in place instead of posting a new one. NOTE: editing an interim "On it\u2026" into the final answer does NOT clear the pending-reply obligation - your final answer must still be a separate slack_reply (interim omitted). Common Slack errors: message_not_found (wrong ts/channel), cant_update_message (not your message), edit_window_closed.',
|
|
47415
47436
|
inputSchema: {
|
|
47416
47437
|
type: "object",
|
|
47417
47438
|
properties: {
|
|
@@ -47421,7 +47442,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47421
47442
|
},
|
|
47422
47443
|
ts: {
|
|
47423
47444
|
type: "string",
|
|
47424
|
-
description: 'Timestamp of the message to edit - the `ts` value
|
|
47445
|
+
description: 'Timestamp of the message to edit - the `ts` value slack_reply returned when you sent it (e.g. "1783902864.258519"). Must be a message the bot itself sent.'
|
|
47425
47446
|
},
|
|
47426
47447
|
text: { type: "string", description: "The new message text. Replaces the existing text." }
|
|
47427
47448
|
},
|
|
@@ -47452,7 +47473,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47452
47473
|
},
|
|
47453
47474
|
{
|
|
47454
47475
|
name: "slack.read_history",
|
|
47455
|
-
description: `Read recent top-level messages in a Slack channel the bot is a member of (via conversations.history). Use this to catch up on what was said in a channel \u2014 e.g. an auto-followed channel you have not been @-mentioned in, or to gather context before posting. This reads CHANNEL history; for one thread's replies use
|
|
47476
|
+
description: `Read recent top-level messages in a Slack channel the bot is a member of (via conversations.history). Use this to catch up on what was said in a channel \u2014 e.g. an auto-followed channel you have not been @-mentioned in, or to gather context before posting. This reads CHANNEL history; for one thread's replies use slack_read_thread instead. Optional oldest/latest bound the time window (Slack ts values). Returns messages oldest\u2192newest. The bot only sees channels it has been invited to \u2014 a "not_in_channel" error means someone must /invite the bot first.`,
|
|
47456
47477
|
inputSchema: {
|
|
47457
47478
|
type: "object",
|
|
47458
47479
|
properties: {
|
|
@@ -47501,7 +47522,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47501
47522
|
...blockKitToolsAvailable() ? [
|
|
47502
47523
|
{
|
|
47503
47524
|
name: "slack.send_structured",
|
|
47504
|
-
description: "Send a Slack message built from Block Kit blocks (structured layout: headers, sections, dividers, context, action buttons). Use when plain text isn't enough \u2014 status reports, confirmations with multiple fields, calls-to-action with buttons. Pass a `text` fallback for push notifications. Returns { message_ts, permalink }. Does NOT block waiting for user interactions; for that use
|
|
47525
|
+
description: "Send a Slack message built from Block Kit blocks (structured layout: headers, sections, dividers, context, action buttons). Use when plain text isn't enough \u2014 status reports, confirmations with multiple fields, calls-to-action with buttons. Pass a `text` fallback for push notifications. Returns { message_ts, permalink }. Does NOT block waiting for user interactions; for that use slack_ask_user.",
|
|
47505
47526
|
inputSchema: {
|
|
47506
47527
|
type: "object",
|
|
47507
47528
|
properties: {
|
|
@@ -47596,7 +47617,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47596
47617
|
},
|
|
47597
47618
|
{
|
|
47598
47619
|
name: "slack.ask_user",
|
|
47599
|
-
description: "Post a question to Slack with multiple-choice buttons and BLOCK until the user taps one (or the timeout fires). Returns the user's choice as { value, label, responded_by, timed_out, responded_at }. Use sparingly \u2014 this stops the agent for up to 5 minutes by default. Prefer plain
|
|
47620
|
+
description: "Post a question to Slack with multiple-choice buttons and BLOCK until the user taps one (or the timeout fires). Returns the user's choice as { value, label, responded_by, timed_out, responded_at }. Use sparingly \u2014 this stops the agent for up to 5 minutes by default. Prefer plain slack_reply for asking questions where you don't need a structured answer back.",
|
|
47600
47621
|
inputSchema: {
|
|
47601
47622
|
type: "object",
|
|
47602
47623
|
properties: {
|
|
@@ -47652,7 +47673,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
47652
47673
|
},
|
|
47653
47674
|
{
|
|
47654
47675
|
name: "slack.channel_info",
|
|
47655
|
-
description: 'Verify the bot can post to a specific Slack channel, OR resolve who is on the other side of a DM. Use this before relying on a channel ID for delivery (e.g. before submitting an AWS access request that will notify an approval channel) to avoid silent posting failures, and to confirm the human behind a DM channel id before claiming a message reached a named person. Works for public AND private channels (unlike
|
|
47676
|
+
description: 'Verify the bot can post to a specific Slack channel, OR resolve who is on the other side of a DM. Use this before relying on a channel ID for delivery (e.g. before submitting an AWS access request that will notify an approval channel) to avoid silent posting failures, and to confirm the human behind a DM channel id before claiming a message reached a named person. Works for public AND private channels (unlike slack_list_channels, which is public-only) and for DM (`D0...`) channels. Returns `{ ok, channel: { id, name, is_private, is_archived, is_member, kind }, bot_user_handle, reason? }`. For a DM, `kind` is "dm" and the result also carries `channel.user` (the partner\'s Slack user id) plus best-effort `channel.user_handle` and `channel.user_name` (the human\'s handle and display name); use these to VERIFY a DM\'s recipient rather than assuming whose DM it is. When `ok=false`, branch on `reason`: "channel_not_found" or "not_in_channel" \u2192 tell the user to run `/invite @<bot_user_handle>` in that channel (the configured ID exists but your bot isn\'t a member, or the ID is from a workspace the bot isn\'t installed in); "archived" \u2192 the channel exists but is archived, ask an admin to point the integration at a non-archived channel; "auth_failed" \u2192 bot token is invalid, escalate to an operator (do not surface to the end user). Pass the raw C0.../G0.../D0... channel ID, not a human name (use slack_list_channels for name \u2192 ID).',
|
|
47656
47677
|
inputSchema: {
|
|
47657
47678
|
type: "object",
|
|
47658
47679
|
properties: {
|
|
@@ -48029,7 +48050,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
48029
48050
|
});
|
|
48030
48051
|
if (redirectNote) base += `. ${redirectNote}`;
|
|
48031
48052
|
if (interim) {
|
|
48032
|
-
const promiseNote = describePromiseStampOutcome(slackPromiseStampOutcome, "
|
|
48053
|
+
const promiseNote = describePromiseStampOutcome(slackPromiseStampOutcome, "slack_reply");
|
|
48033
48054
|
if (promiseNote) base += `. ${promiseNote}`;
|
|
48034
48055
|
if (slackPromiseStampOutcome.attempted > slackPromiseStampOutcome.stamped) {
|
|
48035
48056
|
process.stderr.write(
|
|
@@ -48110,7 +48131,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
48110
48131
|
const { channel, ts, text } = args;
|
|
48111
48132
|
if (!channel || !ts || typeof text !== "string") {
|
|
48112
48133
|
return {
|
|
48113
|
-
content: [{ type: "text", text: "
|
|
48134
|
+
content: [{ type: "text", text: "slack_update requires channel, ts and text" }],
|
|
48114
48135
|
isError: true
|
|
48115
48136
|
};
|
|
48116
48137
|
}
|
|
@@ -48317,13 +48338,13 @@ ${result.formatted}` : "No messages in range, or the bot is not a member of this
|
|
|
48317
48338
|
const { file_id, channel } = args;
|
|
48318
48339
|
if (typeof file_id !== "string" || !file_id.trim()) {
|
|
48319
48340
|
return {
|
|
48320
|
-
content: [{ type: "text", text: "
|
|
48341
|
+
content: [{ type: "text", text: "slack_download_attachment requires a string `file_id`." }],
|
|
48321
48342
|
isError: true
|
|
48322
48343
|
};
|
|
48323
48344
|
}
|
|
48324
48345
|
if (typeof channel !== "string" || !channel.trim()) {
|
|
48325
48346
|
return {
|
|
48326
|
-
content: [{ type: "text", text: "
|
|
48347
|
+
content: [{ type: "text", text: "slack_download_attachment requires a string `channel` (from the inbound <channel> tag)." }],
|
|
48327
48348
|
isError: true
|
|
48328
48349
|
};
|
|
48329
48350
|
}
|
|
@@ -48586,7 +48607,7 @@ async function getPermalink(channel, ts) {
|
|
|
48586
48607
|
async function handleSendStructured(args) {
|
|
48587
48608
|
if (!blockKitToolsAvailable()) {
|
|
48588
48609
|
return {
|
|
48589
|
-
content: [{ type: "text", text: "
|
|
48610
|
+
content: [{ type: "text", text: "slack_send_structured is disabled by the SLACK_BLOCK_KIT_DISABLED fleet kill switch." }],
|
|
48590
48611
|
isError: true
|
|
48591
48612
|
};
|
|
48592
48613
|
}
|
|
@@ -48681,7 +48702,7 @@ async function handleSendStructured(args) {
|
|
|
48681
48702
|
}
|
|
48682
48703
|
async function handleAskUser(args) {
|
|
48683
48704
|
if (!askUserToolAvailable()) {
|
|
48684
|
-
return errResult("
|
|
48705
|
+
return errResult("slack_ask_user is disabled or the host MCP is missing AGT_HOST/AGT_API_KEY/AGT_AGENT_ID env wiring.");
|
|
48685
48706
|
}
|
|
48686
48707
|
const { randomUUID: rndUUID } = await import("crypto");
|
|
48687
48708
|
const runtime = await Promise.resolve().then(() => (init_slack_block_kit_runtime(), slack_block_kit_runtime_exports));
|
|
@@ -15741,12 +15741,12 @@ var mcp = new Server(
|
|
|
15741
15741
|
instructions: [
|
|
15742
15742
|
// High-priority lines first — Claude Code truncates instructions at
|
|
15743
15743
|
// 2048 chars, so anything appended late silently disappears.
|
|
15744
|
-
"CRITICAL: every response to an MS Teams <channel> tag MUST go through
|
|
15745
|
-
"Messages from Teams arrive as a channel tag carrying user (an AAD id), user_name, conversation_id, service_url and reply_to_id. Pass conversation_id + service_url + reply_to_id from the tag to
|
|
15744
|
+
"CRITICAL: every response to an MS Teams <channel> tag MUST go through teams_reply. Text in your session without a teams_reply call never reaches the user.",
|
|
15745
|
+
"Messages from Teams arrive as a channel tag carrying user (an AAD id), user_name, conversation_id, service_url and reply_to_id. Pass conversation_id + service_url + reply_to_id from the tag to teams_reply verbatim; threaded replies must include reply_to_id.",
|
|
15746
15746
|
"Address users by user_name, never by raw user id (AAD object ids leak directory information).",
|
|
15747
15747
|
"service_url is region-specific and arrives on every inbound \u2014 NEVER hardcode it, always echo the inbound value.",
|
|
15748
15748
|
'Channel message + auto_followed="true": only reply if you have something useful to add; otherwise silently skip.',
|
|
15749
|
-
"Reaction taxonomy (use
|
|
15749
|
+
"Reaction taxonomy (use teams_react sparingly \u2014 prefer a reply): like = ack; heart = success. NEVER react to signal failure \u2014 users can't tell why something failed from a reaction. On failure, teams_reply with one sentence explaining what went wrong."
|
|
15750
15750
|
].join(" ")
|
|
15751
15751
|
}
|
|
15752
15752
|
);
|
|
@@ -16195,7 +16195,7 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
16195
16195
|
async function handleSendStructured(args) {
|
|
16196
16196
|
if (!ADAPTIVE_CARDS_ENABLED) {
|
|
16197
16197
|
return errResult(
|
|
16198
|
-
"
|
|
16198
|
+
"teams_send_structured is disabled. Set MSTEAMS_ADAPTIVE_CARDS_ENABLED=true to enable Adaptive Cards."
|
|
16199
16199
|
);
|
|
16200
16200
|
}
|
|
16201
16201
|
const conversationId = stringArg(args, "conversation_id");
|
|
@@ -16223,7 +16223,7 @@ async function handleSendStructured(args) {
|
|
|
16223
16223
|
async function handleAskUser(args) {
|
|
16224
16224
|
if (!ADAPTIVE_CARDS_ENABLED || !ADAPTIVE_CARDS_ASK_USER_ENABLED) {
|
|
16225
16225
|
return errResult(
|
|
16226
|
-
"
|
|
16226
|
+
"teams_ask_user is disabled. Set MSTEAMS_ADAPTIVE_CARDS_ENABLED=true AND MSTEAMS_ADAPTIVE_CARDS_ASK_USER_ENABLED=true to enable."
|
|
16227
16227
|
);
|
|
16228
16228
|
}
|
|
16229
16229
|
if (!PENDING_INTERACTIONS_DIR) {
|
|
@@ -39191,11 +39191,18 @@ var FLAG_REGISTRY = [
|
|
|
39191
39191
|
},
|
|
39192
39192
|
{
|
|
39193
39193
|
key: "ip-allowlist-enforce-admin-debug",
|
|
39194
|
-
description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture.
|
|
39194
|
+
description: "ENG-10594 - whether the IP allowlist on the admin-debug surface (/admin/debug/*, including the deliberately unauthenticated POST /admin/debug/token that exchanges a tlk_ key) REFUSES, or merely observes. OFF (default) = shadow: every request is still evaluated and logged with verdict=admit or verdict=would-refuse, and nothing is ever rejected. ON = a caller whose platform-attested source address is outside AGT_IP_ALLOWLIST_ADMIN_DEBUG gets a bare 403. THE SSM PARAMETER IS A SECOND LEVER AND BOTH ARE NEEDED: with the list unset this flag does nothing, because an unconfigured allowlist admits everyone by design. Arm in that order - populate the list, read the shadow logs until the would-refuse set is understood, then flip this. Deliberately global (evaluated with an empty context): the middleware runs BEFORE authentication, so there is no verified org to scope by, and taking one from an unverified request would let a caller choose its own enforcement posture. There is deliberately NO counterpart flag for the per-agent MCP surface: that one is shadow-only structurally (its ENFORCE_FLAG entry is null), so arming this one does not and cannot arm that one. See the comment below this entry for why.",
|
|
39195
39195
|
flagType: "boolean",
|
|
39196
|
-
// Shadow. The measurement comes first
|
|
39197
|
-
//
|
|
39198
|
-
//
|
|
39196
|
+
// Shadow. The measurement comes first - and ENG-10664 corrected what the
|
|
39197
|
+
// measurement is OF. This comment used to say "nobody yet knows the real set
|
|
39198
|
+
// of staff addresses": there is no such set. Measured over 48h, all 315
|
|
39199
|
+
// requests to this surface came from ONE address, the agt-hosts AZ-2a NAT
|
|
39200
|
+
// EIP - an AGENT calling the debug_* tools, not a person. The decided policy
|
|
39201
|
+
// is that it stays that way (admin-debug is only ever reached from our own
|
|
39202
|
+
// NAT gateways). So the pre-arm check is not "does my laptop appear" but
|
|
39203
|
+
// "is every NAT EIP in the list", because an AZ whose NAT has not yet
|
|
39204
|
+
// carried a debug_* call is absent from the log and would be refused the
|
|
39205
|
+
// instant this flips. See docs/runbooks/ip-allowlist-rollout.md step 3.
|
|
39199
39206
|
defaultValue: false,
|
|
39200
39207
|
// Arming it changes who can reach production diagnostics. Worth one
|
|
39201
39208
|
// confirmation.
|
|
@@ -39210,20 +39217,34 @@ var FLAG_REGISTRY = [
|
|
|
39210
39217
|
// ENFORCE_FLAG (packages/api/src/middleware/ip-allowlist.ts) is null, so no
|
|
39211
39218
|
// flag is consulted and no flag flip can arm it.
|
|
39212
39219
|
//
|
|
39213
|
-
// Why it is not simply a second flag defaulting false
|
|
39214
|
-
//
|
|
39215
|
-
//
|
|
39216
|
-
//
|
|
39217
|
-
//
|
|
39218
|
-
//
|
|
39219
|
-
//
|
|
39220
|
+
// Why it is not simply a second flag defaulting false (CORRECTED, ENG-10664):
|
|
39221
|
+
//
|
|
39222
|
+
// This comment used to say the callers are agent HOSTS, most of which have no
|
|
39223
|
+
// allowlistable address per ADR-0032. That is the wrong caller. Agent hosts do
|
|
39224
|
+
// not call this surface at all: its handler's first act is
|
|
39225
|
+
// validateMcpAccessToken() against `mcp_oauth_tokens` (routes/mcp.ts:87, token
|
|
39226
|
+
// check at :91), so a host JWT is rejected outright - routes/host-runtime.ts
|
|
39227
|
+
// :33421 says so in as many words, which is why the ENG-4724 /host/* twins
|
|
39228
|
+
// exist. The caller set is remote MCP OAuth clients, structurally.
|
|
39229
|
+
//
|
|
39230
|
+
// The conclusion is unchanged and ONLY the reason has moved. Allowlisting this
|
|
39231
|
+
// surface would mean pinning a THIRD PARTY's egress ranges rather than our own
|
|
39232
|
+
// NAT EIPs - a different control, a different owner, a different failure mode:
|
|
39233
|
+
// they move a range and agent MCP access breaks with no signal on our side.
|
|
39234
|
+
// Per docs/runbooks/composio-allowlist-rollout.md this class of change has no
|
|
39220
39235
|
// fleet-level undo. A flag would leave that one toggle away during an
|
|
39221
39236
|
// incident, from someone who had just armed the admin-debug surface and
|
|
39222
39237
|
// reasonably assumed the pair moved together. Requiring a code change puts
|
|
39223
39238
|
// review in front of an irreversible action, which is the right price.
|
|
39224
39239
|
//
|
|
39225
|
-
//
|
|
39226
|
-
//
|
|
39240
|
+
// Keeping the false rationale would have been worse than having none: a reader
|
|
39241
|
+
// who checked it would find it does not hold and might "fix" it by adding the
|
|
39242
|
+
// flag. That is the failure this rewrite exists to prevent.
|
|
39243
|
+
//
|
|
39244
|
+
// If we ever DECIDE to take on that third-party dependency deliberately,
|
|
39245
|
+
// adding the flag here and a key in ENFORCE_FLAG is a small diff - but it is a
|
|
39246
|
+
// decision to own someone else's egress, not a discovery that the population
|
|
39247
|
+
// was allowlistable all along.
|
|
39227
39248
|
];
|
|
39228
39249
|
var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
|
|
39229
39250
|
|
|
@@ -40796,13 +40817,13 @@ function validateLeaveChatArgs(args) {
|
|
|
40796
40817
|
if (typeof chat_id !== "string" || !chat_id.trim()) {
|
|
40797
40818
|
return {
|
|
40798
40819
|
ok: false,
|
|
40799
|
-
message: "
|
|
40820
|
+
message: "telegram_leave_chat requires chat_id (from the <channel> tag)."
|
|
40800
40821
|
};
|
|
40801
40822
|
}
|
|
40802
40823
|
if (typeof reason !== "string" || !reason.trim()) {
|
|
40803
40824
|
return {
|
|
40804
40825
|
ok: false,
|
|
40805
|
-
message: "
|
|
40826
|
+
message: "telegram_leave_chat requires a reason (a non-empty string). It is logged, not sent to the chat."
|
|
40806
40827
|
};
|
|
40807
40828
|
}
|
|
40808
40829
|
return { ok: true, chatId: chat_id.trim(), reason: reason.trim() };
|
|
@@ -42555,19 +42576,19 @@ async function actuateHostRestart(opts) {
|
|
|
42555
42576
|
|
|
42556
42577
|
// src/telegram-mcp-instructions.ts
|
|
42557
42578
|
var MAX_SKIP_REACTION_UNITS = 8;
|
|
42558
|
-
var REACT_POLICY = `Reacting is egress and does NOT settle anything \u2014
|
|
42579
|
+
var REACT_POLICY = `Reacting is egress and does NOT settle anything \u2014 telegram_consume is what settles. Never react in a chat you are barred from, and never post a "this wasn't for me" message.`;
|
|
42559
42580
|
function buildTelegramMcpInstructions(opts) {
|
|
42560
42581
|
const emoji2 = opts.skipReaction ?? "";
|
|
42561
42582
|
const skipReactionOn = opts.skipReactionOn && emoji2.length > 0 && emoji2.length <= MAX_SKIP_REACTION_UNITS;
|
|
42562
42583
|
return [
|
|
42563
|
-
"CRITICAL: every response to a Telegram <channel> tag MUST go through
|
|
42584
|
+
"CRITICAL: every response to a Telegram <channel> tag MUST go through telegram_reply with the chat_id from the tag \u2014 typed text never reaches the user. Slow work: interim ack (telegram_reply interim:true), then your FINAL answer as a separate telegram_reply (interim omitted). An ack is not the answer.",
|
|
42564
42585
|
"Messages from Telegram arrive as a channel tag carrying chat_id, user, user_name and message_id. Pass reply_to_message_id from the tag so the response lands as a quote-reply in busy chats.",
|
|
42565
|
-
"Deliberately NOT replying (a standing rule bars you from that chat, or the message only changed state)? Call
|
|
42566
|
-
"Inbound attachments: `files` on the tag is a JSON string \u2014 JSON.parse it. An entry with `path` (or top-level `image_path`) is already downloaded \u2014 Read it. Entries with `file_id` and no `path` need
|
|
42586
|
+
"Deliberately NOT replying (a standing rule bars you from that chat, or the message only changed state)? Call telegram_consume with the chat_id and a short reason: it settles the message without posting anything. Without a reply or a consume it counts as undelivered and re-delivers to you forever.",
|
|
42587
|
+
"Inbound attachments: `files` on the tag is a JSON string \u2014 JSON.parse it. An entry with `path` (or top-level `image_path`) is already downloaded \u2014 Read it. Entries with `file_id` and no `path` need telegram_download_attachment (file_id + chat_id), then Read the returned path.",
|
|
42567
42588
|
'For work >30s follow the CLAUDE.md kanban flow: kanban_add \u2192 reply "On it \u2014 tracking here: <kanban URL>" \u2192 in_progress \u2192 reply the result. Simple lookups skip kanban but still reply.',
|
|
42568
42589
|
"Address users by user_name; user is the numeric Telegram ID. Resolve ambiguous times against your own Timezone from CLAUDE.md \u2014 do not ask.",
|
|
42569
|
-
"Reactions (
|
|
42570
|
-
skipReactionOn ? `${REACT_POLICY} Where engaging IS permitted you may also
|
|
42590
|
+
"Reactions (telegram_react sparingly \u2014 prefer a reply): \u{1F440} = ack (auto-added, do not duplicate); \u{1F44D} or \u{1F389} = success." + (skipReactionOn ? ` ${emoji2} = seen, not replying.` : "") + " NEVER react to signal failure of YOUR work \u2014 telegram_reply one sentence explaining it. Free-tier emoji only: \u{1F44D} \u{1F44E} \u2764 \u{1F525} \u{1F389} \u{1F64F} \u{1F44C} \u{1F440} \u{1F4AF} \u{1FAE1}.",
|
|
42591
|
+
skipReactionOn ? `${REACT_POLICY} Where engaging IS permitted you may also telegram_react "${emoji2}" after consuming.` : REACT_POLICY
|
|
42571
42592
|
].filter(Boolean).join(" ");
|
|
42572
42593
|
}
|
|
42573
42594
|
|
|
@@ -45100,7 +45121,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
45100
45121
|
},
|
|
45101
45122
|
interim: {
|
|
45102
45123
|
type: "boolean",
|
|
45103
|
-
description: 'Set true ONLY when this is an interim acknowledgement (e.g. "On it \u2014 checking now\u2026") and the real answer is still coming. The system keeps tracking this chat as awaiting your final reply. Your eventual substantive answer MUST be a separate
|
|
45124
|
+
description: 'Set true ONLY when this is an interim acknowledgement (e.g. "On it \u2014 checking now\u2026") and the real answer is still coming. The system keeps tracking this chat as awaiting your final reply. Your eventual substantive answer MUST be a separate telegram_reply with interim omitted/false \u2014 that is what marks the request complete. Omit (default false) for any reply that fully answers the user.'
|
|
45104
45125
|
}
|
|
45105
45126
|
},
|
|
45106
45127
|
required: ["chat_id", "text"]
|
|
@@ -45108,7 +45129,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
45108
45129
|
},
|
|
45109
45130
|
{
|
|
45110
45131
|
name: "telegram.consume",
|
|
45111
|
-
description: "Acknowledge Telegram message(s) you handled WITHOUT sending a reply \u2014 a message you deliberately did not answer because a standing rule forbids engaging in that chat, or one that only updated state. Pass the chat_id from the <channel> tag. If you send a
|
|
45132
|
+
description: "Acknowledge Telegram message(s) you handled WITHOUT sending a reply \u2014 a message you deliberately did not answer because a standing rule forbids engaging in that chat, or one that only updated state. Pass the chat_id from the <channel> tag. If you send a telegram_reply you do NOT also need to consume: the reply already acknowledges the message. Without one of the two, a silently-handled message is treated as undelivered and is re-delivered to you indefinitely. Clears the chat, not one message \u2014 that is Telegram's conversation grain \u2014 and only messages received up to the one you are answering, so an inbound that arrives while you are deciding is never discarded unseen.",
|
|
45112
45133
|
inputSchema: {
|
|
45113
45134
|
type: "object",
|
|
45114
45135
|
properties: {
|
|
@@ -45211,7 +45232,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
45211
45232
|
},
|
|
45212
45233
|
{
|
|
45213
45234
|
name: "telegram.react",
|
|
45214
|
-
description: `Add an emoji reaction to a Telegram message. Use sparingly \u2014 prefer a text reply via
|
|
45235
|
+
description: `Add an emoji reaction to a Telegram message. Use sparingly \u2014 prefer a text reply via telegram_reply. Reaction taxonomy: \u{1F44D} or \u{1F389} = action completed successfully. NEVER react to signal failure of your work \u2014 users can't tell why it failed from a reaction. On failure, call telegram_reply with one sentence explaining what went wrong instead. \u{1F440} is already auto-applied on inbound; do not duplicate. Only free-tier emoji reactions are available to bots (Premium-only emoji fail silently). Pass an empty string or omit emoji to clear the bot's reaction on that message. (Note: when a message arrives while the agent is offline/wedged, the runtime \u2014 not you \u2014 posts a brief "can't respond right now" notice; you never need to add a failure reaction for that.)`,
|
|
45215
45236
|
inputSchema: {
|
|
45216
45237
|
type: "object",
|
|
45217
45238
|
properties: {
|
|
@@ -45427,7 +45448,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
45427
45448
|
const { chat_id, reason } = args;
|
|
45428
45449
|
if (!chat_id) {
|
|
45429
45450
|
return {
|
|
45430
|
-
content: [{ type: "text", text: "
|
|
45451
|
+
content: [{ type: "text", text: "telegram_consume requires chat_id (from the <channel> tag)." }],
|
|
45431
45452
|
isError: true
|
|
45432
45453
|
};
|
|
45433
45454
|
}
|
|
@@ -45563,7 +45584,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
45563
45584
|
if (name === "telegram.reply") {
|
|
45564
45585
|
if (interim) {
|
|
45565
45586
|
markPromisedPendingMessage(chat_id, drainCutoffMs);
|
|
45566
|
-
promiseNote = describePromiseStampOutcome(telegramPromiseStampOutcome, "
|
|
45587
|
+
promiseNote = describePromiseStampOutcome(telegramPromiseStampOutcome, "telegram_reply");
|
|
45567
45588
|
} else {
|
|
45568
45589
|
clearPendingMessage(chat_id, drainCutoffMs);
|
|
45569
45590
|
}
|
|
@@ -45623,13 +45644,13 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
45623
45644
|
const { file_id, chat_id } = args;
|
|
45624
45645
|
if (typeof file_id !== "string" || !file_id.trim()) {
|
|
45625
45646
|
return {
|
|
45626
|
-
content: [{ type: "text", text: "
|
|
45647
|
+
content: [{ type: "text", text: "telegram_download_attachment requires a string `file_id`." }],
|
|
45627
45648
|
isError: true
|
|
45628
45649
|
};
|
|
45629
45650
|
}
|
|
45630
45651
|
if (typeof chat_id !== "string" || !chat_id.trim()) {
|
|
45631
45652
|
return {
|
|
45632
|
-
content: [{ type: "text", text: "
|
|
45653
|
+
content: [{ type: "text", text: "telegram_download_attachment requires a string `chat_id` (from the inbound <channel> tag)." }],
|
|
45633
45654
|
isError: true
|
|
45634
45655
|
};
|
|
45635
45656
|
}
|
|
@@ -156049,19 +156049,19 @@ var mcp = new Server(
|
|
|
156049
156049
|
tools: {}
|
|
156050
156050
|
},
|
|
156051
156051
|
instructions: (IS_BAILEYS ? [
|
|
156052
|
-
"CRITICAL: every response to a WhatsApp <channel> tag MUST go through
|
|
156053
|
-
"Messages arrive as a channel tag carrying sender (a JID), conversation_id and message_id. Pass the `sender` value verbatim as the `to` argument to
|
|
156054
|
-
"
|
|
156052
|
+
"CRITICAL: every response to a WhatsApp <channel> tag MUST go through whatsapp_reply. Text in your session without a tool call never reaches the user.",
|
|
156053
|
+
"Messages arrive as a channel tag carrying sender (a JID), conversation_id and message_id. Pass the `sender` value verbatim as the `to` argument to whatsapp_reply.",
|
|
156054
|
+
"whatsapp_reply sends free-form text with no restrictions (this is a linked personal number, not a business template flow)."
|
|
156055
156055
|
] : [
|
|
156056
|
-
"CRITICAL: every response to a WhatsApp <channel> tag MUST go through
|
|
156057
|
-
"Messages arrive as a channel tag carrying sender, conversation_id and message_id. Pass the `sender` value verbatim as the `to` argument to
|
|
156058
|
-
"
|
|
156056
|
+
"CRITICAL: every response to a WhatsApp <channel> tag MUST go through whatsapp_reply (or whatsapp_send_template). Text in your session without a tool call never reaches the user.",
|
|
156057
|
+
"Messages arrive as a channel tag carrying sender, conversation_id and message_id. Pass the `sender` value verbatim as the `to` argument to whatsapp_reply.",
|
|
156058
|
+
"whatsapp_reply sends free-form text and only works inside the 24h customer-service window (24h since the user last messaged). If it reports the window is closed, use whatsapp_send_template with a pre-approved template instead."
|
|
156059
156059
|
]).join(" ")
|
|
156060
156060
|
}
|
|
156061
156061
|
);
|
|
156062
156062
|
var replyTool = {
|
|
156063
156063
|
name: "whatsapp.reply",
|
|
156064
|
-
description: IS_BAILEYS ? "Send a WhatsApp text reply. Pass the inbound `sender` (a JID) value as `to`. Free-form, no restrictions." : "Send a free-form WhatsApp text reply. Pass the inbound `sender` value as `to`. Only deliverable inside the 24h customer-service window; outside it, use
|
|
156064
|
+
description: IS_BAILEYS ? "Send a WhatsApp text reply. Pass the inbound `sender` (a JID) value as `to`. Free-form, no restrictions." : "Send a free-form WhatsApp text reply. Pass the inbound `sender` value as `to`. Only deliverable inside the 24h customer-service window; outside it, use whatsapp_send_template.",
|
|
156065
156065
|
inputSchema: {
|
|
156066
156066
|
type: "object",
|
|
156067
156067
|
properties: {
|
|
@@ -156123,7 +156123,7 @@ async function handleReply(a) {
|
|
|
156123
156123
|
const lastInbound = lastInboundBySender.get(to) ?? null;
|
|
156124
156124
|
if (chooseOutboundMode(lastInbound, Date.now()) === "template") {
|
|
156125
156125
|
return errResult(
|
|
156126
|
-
"The 24h customer-service window for this recipient is closed (no inbound within 24h). Free-form text cannot be delivered \u2014 use
|
|
156126
|
+
"The 24h customer-service window for this recipient is closed (no inbound within 24h). Free-form text cannot be delivered \u2014 use whatsapp_send_template with a pre-approved template."
|
|
156127
156127
|
);
|
|
156128
156128
|
}
|
|
156129
156129
|
const res = await kapso.sendText(to, text);
|
|
@@ -156131,7 +156131,7 @@ async function handleReply(a) {
|
|
|
156131
156131
|
return { content: [{ type: "text", text: `sent (message_id=${res.messageId ?? "unknown"})` }] };
|
|
156132
156132
|
}
|
|
156133
156133
|
async function handleSendTemplate(a) {
|
|
156134
|
-
if (IS_BAILEYS) return errResult("Templates are not used in QR-link mode \u2014 use
|
|
156134
|
+
if (IS_BAILEYS) return errResult("Templates are not used in QR-link mode \u2014 use whatsapp_reply to send freely.");
|
|
156135
156135
|
if (!kapso) return errResult("WhatsApp outbound is not configured (missing WHATSAPP_PROJECT_API_KEY / WHATSAPP_PHONE_NUMBER_ID).");
|
|
156136
156136
|
const to = stringArg(a, "to");
|
|
156137
156137
|
const name = stringArg(a, "template");
|
|
@@ -62,8 +62,8 @@ import {
|
|
|
62
62
|
writeDirectChatSessionState,
|
|
63
63
|
writeEgressAllowlist,
|
|
64
64
|
writePersistentClaudeWrapper
|
|
65
|
-
} from "./chunk-
|
|
66
|
-
import "./chunk-
|
|
65
|
+
} from "./chunk-KTFN2AIB.js";
|
|
66
|
+
import "./chunk-VF5BT7BP.js";
|
|
67
67
|
import "./chunk-DHWNVVX4.js";
|
|
68
68
|
import "./chunk-XWVM4KPK.js";
|
|
69
69
|
export {
|
|
@@ -131,4 +131,4 @@ export {
|
|
|
131
131
|
writeEgressAllowlist,
|
|
132
132
|
writePersistentClaudeWrapper
|
|
133
133
|
};
|
|
134
|
-
//# sourceMappingURL=persistent-session-
|
|
134
|
+
//# sourceMappingURL=persistent-session-26W47NVS.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
paneLogPath
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-KTFN2AIB.js";
|
|
4
|
+
import "./chunk-VF5BT7BP.js";
|
|
5
5
|
import "./chunk-DHWNVVX4.js";
|
|
6
6
|
import "./chunk-XWVM4KPK.js";
|
|
7
7
|
|
|
@@ -800,4 +800,4 @@ export {
|
|
|
800
800
|
readAndResetSlackReplyBindingClassifications,
|
|
801
801
|
readAndResetSlackReplyTargetClassifications
|
|
802
802
|
};
|
|
803
|
-
//# sourceMappingURL=responsiveness-probe-
|
|
803
|
+
//# sourceMappingURL=responsiveness-probe-KMHYGGYP.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sessionTranscriptDir
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VF5BT7BP.js";
|
|
4
4
|
|
|
5
5
|
// src/lib/session-auth-dead.ts
|
|
6
6
|
import { closeSync, openSync, readSync, readdirSync, statSync } from "fs";
|
|
@@ -203,4 +203,4 @@ export {
|
|
|
203
203
|
decideSessionAuthState,
|
|
204
204
|
probeSessionAuth
|
|
205
205
|
};
|
|
206
|
-
//# sourceMappingURL=session-auth-dead-
|
|
206
|
+
//# sourceMappingURL=session-auth-dead-CRG2ESD2.js.map
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|