switchroom 0.15.36 → 0.15.38
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/agent-scheduler/index.js +10 -9
- package/dist/auth-broker/index.js +9 -9
- package/dist/cli/autoaccept-poll.js +13 -7
- package/dist/cli/notion-write-pretool.mjs +9 -9
- package/dist/cli/switchroom.js +480 -217
- package/dist/cli/ui/index.html +87 -17
- package/dist/host-control/main.js +10 -10
- package/dist/vault/approvals/kernel-server.js +9 -9
- package/dist/vault/broker/server.js +9 -9
- package/package.json +1 -1
- package/profiles/_base/cron-session.sh.hbs +1 -1
- package/profiles/_base/start.sh.hbs +1 -1
- package/profiles/_shared/agent-self-service.md.hbs +25 -0
- package/skills/switchroom-manage/SKILL.md +1 -1
- package/skills/switchroom-runtime/SKILL.md +1 -1
- package/telegram-plugin/answer-stream.ts +1 -1
- package/telegram-plugin/bridge/bridge.ts +50 -1
- package/telegram-plugin/bridge/ipc-client.ts +4 -1
- package/telegram-plugin/bridge/tool-filter.ts +77 -0
- package/telegram-plugin/chat-lock.ts +1 -1
- package/telegram-plugin/credits-watch.ts +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +60 -3
- package/telegram-plugin/dist/gateway/gateway.js +753 -207
- package/telegram-plugin/dist/server.js +64 -4
- package/telegram-plugin/gateway/auto-classify-mid-turn.ts +1 -1
- package/telegram-plugin/gateway/boot-card.ts +5 -1
- package/telegram-plugin/gateway/boot-probes.ts +62 -0
- package/telegram-plugin/gateway/cron-session.ts +1 -1
- package/telegram-plugin/gateway/gateway.ts +254 -15
- package/telegram-plugin/gateway/grant-restart.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine-dispatch.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine-shadow.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine.ts +1 -1
- package/telegram-plugin/gateway/interrupt-defer.ts +1 -1
- package/telegram-plugin/gateway/ipc-protocol.ts +12 -0
- package/telegram-plugin/gateway/linear-activity.ts +56 -0
- package/telegram-plugin/gateway/linear-auth-watch.ts +102 -0
- package/telegram-plugin/gateway/linear-setup.ts +196 -0
- package/telegram-plugin/gateway/permission-card-origin.ts +62 -0
- package/telegram-plugin/gateway/permission-timeout.ts +70 -0
- package/telegram-plugin/gateway/prefix-warmup.ts +1 -1
- package/telegram-plugin/gateway/webhook-ingest-server.test.ts +1 -1
- package/telegram-plugin/gateway/webhook-ingest-server.ts +1 -1
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +1 -1
- package/telegram-plugin/interrupt-marker.ts +1 -1
- package/telegram-plugin/over-ping-safety-net.ts +1 -1
- package/telegram-plugin/scoped-approval.ts +1 -1
- package/telegram-plugin/secret-detect/vault-error.ts +1 -1
- package/telegram-plugin/silence-poke.ts +2 -2
- package/telegram-plugin/silent-reply-anchor.ts +1 -1
- package/telegram-plugin/slot-banner-driver.ts +1 -1
- package/telegram-plugin/startup-reset.ts +1 -1
- package/telegram-plugin/tests/boot-probes-connections.test.ts +66 -0
- package/telegram-plugin/tests/gateway-startup-reset.test.ts +1 -1
- package/telegram-plugin/tests/inbound-delivery-machine.test.ts +1 -1
- package/telegram-plugin/tests/linear-agent-activity.test.ts +77 -0
- package/telegram-plugin/tests/linear-agent-setup.test.ts +132 -0
- package/telegram-plugin/tests/linear-auth-watch.test.ts +79 -0
- package/telegram-plugin/tests/linear-create-issue.test.ts +3 -1
- package/telegram-plugin/tests/permission-card-origin.test.ts +97 -0
- package/telegram-plugin/tests/permission-card-routing.test.ts +23 -0
- package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +76 -0
- package/telegram-plugin/tests/permission-timeout.test.ts +87 -0
- package/telegram-plugin/tests/scoped-approval.test.ts +1 -1
- package/telegram-plugin/tests/silence-poke.test.ts +1 -1
- package/telegram-plugin/tests/tool-filter.test.ts +87 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +1 -1
- package/telegram-plugin/turn-flush-safety.ts +1 -1
- package/telegram-plugin/uat/assertions.ts +1 -1
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-fast-ack-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-fast-trivial-dm.test.ts +2 -2
- package/telegram-plugin/uat/scenarios/jtbd-forwarded-burst-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-memory-survives-restart-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-reflective-status-reaction-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-wake-audit-content-dm.test.ts +1 -1
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type PtyTailHandle,
|
|
28
28
|
} from '../pty-tail.js'
|
|
29
29
|
import { createIpcClient, type IpcClientHandle } from './ipc-client.js'
|
|
30
|
+
import { buildEffectiveToolSchemas, LINEAR_ENV } from './tool-filter.js'
|
|
30
31
|
import type { InboundMessage, PermissionEvent, StatusEvent } from '../gateway/ipc-protocol.js'
|
|
31
32
|
import { matchesAllowRule } from '../permission-rule.js'
|
|
32
33
|
|
|
@@ -510,9 +511,51 @@ const TOOL_SCHEMAS = [
|
|
|
510
511
|
required: ['title', 'body'],
|
|
511
512
|
},
|
|
512
513
|
},
|
|
514
|
+
{
|
|
515
|
+
name: 'linear_agent_setup',
|
|
516
|
+
description:
|
|
517
|
+
'Provision THIS agent as a Linear app actor (actor=app OAuth) from inside the container — the operator-approved in-container path that replaces the host-only `switchroom linear-agent setup` (which silently no-ops in a sandbox). Two steps. action="authorize_url": pass the OAuth app client_id + redirect_uri; returns the browser URL the operator opens to consent. action="complete": pass client_id, client_secret, redirect_uri, and the code from the redirect; exchanges it and stores the access token (linear/<agent>/token) + the durable refresh bundle (linear/<agent>/oauth) via the vault broker so the token auto-renews. Writing those NEW keys needs a write-grant — if the broker denies, the tool returns the exact vault_request_access calls to make (operator approves), then re-run "complete". After it stores the values, follow the returned guidance to config_propose_edit the linear_agent block + secrets[] ACL (also operator-approved) to make it durable. The client_secret and code are used in-process only — never stored in config or logged.',
|
|
518
|
+
inputSchema: {
|
|
519
|
+
type: 'object',
|
|
520
|
+
properties: {
|
|
521
|
+
action: {
|
|
522
|
+
type: 'string',
|
|
523
|
+
enum: ['authorize_url', 'complete'],
|
|
524
|
+
description: '"authorize_url" to get the browser consent URL; "complete" to exchange the code and store the credentials.',
|
|
525
|
+
},
|
|
526
|
+
client_id: {
|
|
527
|
+
type: 'string',
|
|
528
|
+
description: 'Linear OAuth app client id (from Linear → Settings → API → your agent app).',
|
|
529
|
+
},
|
|
530
|
+
redirect_uri: {
|
|
531
|
+
type: 'string',
|
|
532
|
+
description: 'The redirect URI registered on the Linear OAuth app (e.g. http://localhost:3000/callback). Must match exactly in both steps.',
|
|
533
|
+
},
|
|
534
|
+
client_secret: {
|
|
535
|
+
type: 'string',
|
|
536
|
+
description: 'Linear OAuth app client secret. Required for action="complete"; used in-process for the token exchange, never stored or logged.',
|
|
537
|
+
},
|
|
538
|
+
code: {
|
|
539
|
+
type: 'string',
|
|
540
|
+
description: 'The authorization code from the redirect URL (the `code=` query param). Required for action="complete"; single-use.',
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
required: ['action', 'client_id', 'redirect_uri'],
|
|
544
|
+
},
|
|
545
|
+
},
|
|
513
546
|
]
|
|
514
547
|
|
|
515
|
-
|
|
548
|
+
// Tool-surface right-sizing (P4): connection-gate linear_* + per-tool
|
|
549
|
+
// alwaysLoad pins for the hot path. See tool-filter.ts for the rationale.
|
|
550
|
+
// Computed once at startup — SWITCHROOM_TELEGRAM_LINEAR is fixed for the
|
|
551
|
+
// process lifetime (set by the gateway in .mcp.json when Linear is wired).
|
|
552
|
+
const EFFECTIVE_TOOL_SCHEMAS = buildEffectiveToolSchemas(TOOL_SCHEMAS, {
|
|
553
|
+
linearEnabled: process.env[LINEAR_ENV] === '1',
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
557
|
+
tools: EFFECTIVE_TOOL_SCHEMAS,
|
|
558
|
+
}))
|
|
516
559
|
|
|
517
560
|
// ─── MCP CallTool → IPC forward ─────────────────────────────────────────
|
|
518
561
|
|
|
@@ -659,6 +702,12 @@ function onPermission(msg: PermissionEvent): void {
|
|
|
659
702
|
params: {
|
|
660
703
|
request_id: msg.requestId,
|
|
661
704
|
behavior: msg.behavior,
|
|
705
|
+
// `message` (deny only) is rendered by claude's channel as
|
|
706
|
+
// "…the user said: ${message}". We use it to tell the model a deny was
|
|
707
|
+
// a TIMEOUT, not a human denial — so it doesn't retry the identical
|
|
708
|
+
// call and re-raise a duplicate card. Omitted → claude's default
|
|
709
|
+
// "Denied" (safe degradation).
|
|
710
|
+
...(msg.message ? { message: msg.message } : {}),
|
|
662
711
|
},
|
|
663
712
|
}).catch((err) => {
|
|
664
713
|
process.stderr.write(`telegram bridge: failed to deliver permission to Claude: ${err}\n`)
|
|
@@ -26,7 +26,10 @@ export function validateGatewayMessage(msg: unknown): msg is GatewayToClient {
|
|
|
26
26
|
&& (m.behavior === "allow" || m.behavior === "deny")
|
|
27
27
|
// `rule` is optional (only sent on "🔁 Always allow"); when present
|
|
28
28
|
// it must be a non-empty string. #1138 wire extension.
|
|
29
|
-
&& (m.rule === undefined || (typeof m.rule === "string" && m.rule.length > 0))
|
|
29
|
+
&& (m.rule === undefined || (typeof m.rule === "string" && m.rule.length > 0))
|
|
30
|
+
// `message` is optional (deny only — the timeout-vs-denial reason);
|
|
31
|
+
// when present it must be a non-empty string.
|
|
32
|
+
&& (m.message === undefined || (typeof m.message === "string" && m.message.length > 0));
|
|
30
33
|
case "status":
|
|
31
34
|
return typeof m.status === "string";
|
|
32
35
|
case "tool_call_result":
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-surface right-sizing for the switchroom-telegram MCP server (P4).
|
|
3
|
+
*
|
|
4
|
+
* switchroom-telegram is the primary interface, so its tool schemas are
|
|
5
|
+
* always-loaded on EVERY agent, every turn (~8k tokens). Two reductions,
|
|
6
|
+
* both native to Claude Code tool-search (honored in 2.1.177; the MCP SDK
|
|
7
|
+
* preserves `_meta` on tools/list — @modelcontextprotocol/sdk ToolSchema
|
|
8
|
+
* has `_meta: z.record(...).optional()`):
|
|
9
|
+
*
|
|
10
|
+
* A. Connection gating — linear_* tools are advertised only when the
|
|
11
|
+
* agent has the Linear connection configured (the gateway sets
|
|
12
|
+
* SWITCHROOM_TELEGRAM_LINEAR=1 from channels.telegram.linear_agent.
|
|
13
|
+
* enabled). A non-Linear agent never carries their schema at all.
|
|
14
|
+
*
|
|
15
|
+
* B. Per-tool deferral — the server is no longer pinned wholesale
|
|
16
|
+
* (scaffold sets alwaysLoad:false). We pin ONLY the load-bearing hot
|
|
17
|
+
* tools via `_meta["anthropic/alwaysLoad"]` so they never defer (the
|
|
18
|
+
* reply path must never pay a ToolSearch round-trip before it can
|
|
19
|
+
* answer); the ~14 cold tools defer and load on first use.
|
|
20
|
+
*
|
|
21
|
+
* Pure + side-effect free so it's unit-testable in isolation (bridge.ts
|
|
22
|
+
* has import-time side effects). Both reductions are non-destructive — every
|
|
23
|
+
* tool the agent is entitled to still works; cold ones just load on demand.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Minimal shape we need from a tool schema. */
|
|
27
|
+
export interface NamedTool {
|
|
28
|
+
name: string
|
|
29
|
+
[k: string]: unknown
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Hot tools pinned loaded — must never defer. The reply path
|
|
34
|
+
* (reply/stream_reply) plus the frequently-used early-turn ops. Everything
|
|
35
|
+
* NOT in this set defers under tool-search.
|
|
36
|
+
*/
|
|
37
|
+
export const ALWAYS_LOAD_TOOLS: ReadonlySet<string> = new Set([
|
|
38
|
+
'reply',
|
|
39
|
+
'stream_reply',
|
|
40
|
+
'get_recent_messages',
|
|
41
|
+
'react',
|
|
42
|
+
'edit_message',
|
|
43
|
+
'send_typing',
|
|
44
|
+
'download_attachment',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
/** Linear tools — advertised only when the Linear connection is configured. */
|
|
48
|
+
export const LINEAR_TOOLS: ReadonlySet<string> = new Set([
|
|
49
|
+
'linear_agent_activity',
|
|
50
|
+
'linear_create_issue',
|
|
51
|
+
'linear_agent_setup',
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
/** The env var the gateway sets (per .mcp.json) when Linear is configured. */
|
|
55
|
+
export const LINEAR_ENV = 'SWITCHROOM_TELEGRAM_LINEAR'
|
|
56
|
+
|
|
57
|
+
export interface ToolFilterOpts {
|
|
58
|
+
/** True when the Linear connection is configured for this agent. */
|
|
59
|
+
linearEnabled: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Apply connection gating (A) + per-tool deferral pins (B) to a tool list.
|
|
64
|
+
* Returns a NEW array; inputs are not mutated. Order is preserved.
|
|
65
|
+
*/
|
|
66
|
+
export function buildEffectiveToolSchemas<T extends NamedTool>(
|
|
67
|
+
schemas: ReadonlyArray<T>,
|
|
68
|
+
opts: ToolFilterOpts,
|
|
69
|
+
): Array<T & { _meta?: { 'anthropic/alwaysLoad': true } }> {
|
|
70
|
+
return schemas
|
|
71
|
+
.filter((t) => opts.linearEnabled || !LINEAR_TOOLS.has(t.name))
|
|
72
|
+
.map((t) =>
|
|
73
|
+
ALWAYS_LOAD_TOOLS.has(t.name)
|
|
74
|
+
? { ...t, _meta: { 'anthropic/alwaysLoad': true as const } }
|
|
75
|
+
: t,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* autoRetry transformer; if two topics in the same chat both burst
|
|
42
42
|
* past the limit, grammY backs off per the Retry-After header — same
|
|
43
43
|
* worst-case behavior as the old per-chat lock, just hit differently.
|
|
44
|
-
* See
|
|
44
|
+
* See reference/rfcs/supergroup-mode.md and the
|
|
45
45
|
* `tests/outbound-ordering.test.ts` 429-isolation row.
|
|
46
46
|
*/
|
|
47
47
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* silently failed (stdout to /dev/null), and the user only noticed
|
|
14
14
|
* hours later when they wondered why their morning brief never came.
|
|
15
15
|
* Direct violation of the #1 product principle (silent failure is
|
|
16
|
-
* the worst case — see reference/know-what-my-agent-is-doing.md).
|
|
16
|
+
* the worst case — see reference/jobs/know-what-my-agent-is-doing.md).
|
|
17
17
|
*
|
|
18
18
|
* This module is a pure decision layer. It reads the file, compares
|
|
19
19
|
* against the last-notified state on disk, and tells the caller
|
|
@@ -24211,7 +24211,7 @@ function validateGatewayMessage(msg) {
|
|
|
24211
24211
|
case "inbound":
|
|
24212
24212
|
return typeof m.chatId === "string" && typeof m.text === "string";
|
|
24213
24213
|
case "permission":
|
|
24214
|
-
return typeof m.requestId === "string" && (m.behavior === "allow" || m.behavior === "deny") && (m.rule === undefined || typeof m.rule === "string" && m.rule.length > 0);
|
|
24214
|
+
return typeof m.requestId === "string" && (m.behavior === "allow" || m.behavior === "deny") && (m.rule === undefined || typeof m.rule === "string" && m.rule.length > 0) && (m.message === undefined || typeof m.message === "string" && m.message.length > 0);
|
|
24215
24215
|
case "status":
|
|
24216
24216
|
return typeof m.status === "string";
|
|
24217
24217
|
case "tool_call_result":
|
|
@@ -24448,6 +24448,26 @@ function createIpcClient(options) {
|
|
|
24448
24448
|
});
|
|
24449
24449
|
}
|
|
24450
24450
|
|
|
24451
|
+
// bridge/tool-filter.ts
|
|
24452
|
+
var ALWAYS_LOAD_TOOLS = new Set([
|
|
24453
|
+
"reply",
|
|
24454
|
+
"stream_reply",
|
|
24455
|
+
"get_recent_messages",
|
|
24456
|
+
"react",
|
|
24457
|
+
"edit_message",
|
|
24458
|
+
"send_typing",
|
|
24459
|
+
"download_attachment"
|
|
24460
|
+
]);
|
|
24461
|
+
var LINEAR_TOOLS = new Set([
|
|
24462
|
+
"linear_agent_activity",
|
|
24463
|
+
"linear_create_issue",
|
|
24464
|
+
"linear_agent_setup"
|
|
24465
|
+
]);
|
|
24466
|
+
var LINEAR_ENV = "SWITCHROOM_TELEGRAM_LINEAR";
|
|
24467
|
+
function buildEffectiveToolSchemas(schemas3, opts) {
|
|
24468
|
+
return schemas3.filter((t) => opts.linearEnabled || !LINEAR_TOOLS.has(t.name)).map((t) => ALWAYS_LOAD_TOOLS.has(t.name) ? { ...t, _meta: { "anthropic/alwaysLoad": true } } : t);
|
|
24469
|
+
}
|
|
24470
|
+
|
|
24451
24471
|
// permission-rule.ts
|
|
24452
24472
|
import { basename as basename2 } from "node:path";
|
|
24453
24473
|
var FILE_TOOLS = new Set([
|
|
@@ -24987,9 +25007,45 @@ var TOOL_SCHEMAS = [
|
|
|
24987
25007
|
},
|
|
24988
25008
|
required: ["title", "body"]
|
|
24989
25009
|
}
|
|
25010
|
+
},
|
|
25011
|
+
{
|
|
25012
|
+
name: "linear_agent_setup",
|
|
25013
|
+
description: 'Provision THIS agent as a Linear app actor (actor=app OAuth) from inside the container \u2014 the operator-approved in-container path that replaces the host-only `switchroom linear-agent setup` (which silently no-ops in a sandbox). Two steps. action="authorize_url": pass the OAuth app client_id + redirect_uri; returns the browser URL the operator opens to consent. action="complete": pass client_id, client_secret, redirect_uri, and the code from the redirect; exchanges it and stores the access token (linear/<agent>/token) + the durable refresh bundle (linear/<agent>/oauth) via the vault broker so the token auto-renews. Writing those NEW keys needs a write-grant \u2014 if the broker denies, the tool returns the exact vault_request_access calls to make (operator approves), then re-run "complete". After it stores the values, follow the returned guidance to config_propose_edit the linear_agent block + secrets[] ACL (also operator-approved) to make it durable. The client_secret and code are used in-process only \u2014 never stored in config or logged.',
|
|
25014
|
+
inputSchema: {
|
|
25015
|
+
type: "object",
|
|
25016
|
+
properties: {
|
|
25017
|
+
action: {
|
|
25018
|
+
type: "string",
|
|
25019
|
+
enum: ["authorize_url", "complete"],
|
|
25020
|
+
description: '"authorize_url" to get the browser consent URL; "complete" to exchange the code and store the credentials.'
|
|
25021
|
+
},
|
|
25022
|
+
client_id: {
|
|
25023
|
+
type: "string",
|
|
25024
|
+
description: "Linear OAuth app client id (from Linear \u2192 Settings \u2192 API \u2192 your agent app)."
|
|
25025
|
+
},
|
|
25026
|
+
redirect_uri: {
|
|
25027
|
+
type: "string",
|
|
25028
|
+
description: "The redirect URI registered on the Linear OAuth app (e.g. http://localhost:3000/callback). Must match exactly in both steps."
|
|
25029
|
+
},
|
|
25030
|
+
client_secret: {
|
|
25031
|
+
type: "string",
|
|
25032
|
+
description: 'Linear OAuth app client secret. Required for action="complete"; used in-process for the token exchange, never stored or logged.'
|
|
25033
|
+
},
|
|
25034
|
+
code: {
|
|
25035
|
+
type: "string",
|
|
25036
|
+
description: 'The authorization code from the redirect URL (the `code=` query param). Required for action="complete"; single-use.'
|
|
25037
|
+
}
|
|
25038
|
+
},
|
|
25039
|
+
required: ["action", "client_id", "redirect_uri"]
|
|
25040
|
+
}
|
|
24990
25041
|
}
|
|
24991
25042
|
];
|
|
24992
|
-
|
|
25043
|
+
var EFFECTIVE_TOOL_SCHEMAS = buildEffectiveToolSchemas(TOOL_SCHEMAS, {
|
|
25044
|
+
linearEnabled: process.env[LINEAR_ENV] === "1"
|
|
25045
|
+
});
|
|
25046
|
+
mcp.setRequestHandler(ListToolsRequestSchema2, async () => ({
|
|
25047
|
+
tools: EFFECTIVE_TOOL_SCHEMAS
|
|
25048
|
+
}));
|
|
24993
25049
|
mcp.setRequestHandler(CallToolRequestSchema2, async (req) => {
|
|
24994
25050
|
const tool = req.params.name;
|
|
24995
25051
|
const args = req.params.arguments ?? {};
|
|
@@ -25077,7 +25133,8 @@ function onPermission(msg) {
|
|
|
25077
25133
|
method: "notifications/claude/channel/permission",
|
|
25078
25134
|
params: {
|
|
25079
25135
|
request_id: msg.requestId,
|
|
25080
|
-
behavior: msg.behavior
|
|
25136
|
+
behavior: msg.behavior,
|
|
25137
|
+
...msg.message ? { message: msg.message } : {}
|
|
25081
25138
|
}
|
|
25082
25139
|
}).catch((err) => {
|
|
25083
25140
|
process.stderr.write(`telegram bridge: failed to deliver permission to Claude: ${err}
|