Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions container/agent-runner/src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ export function isRunnerCommand(msg: MessageInRow): boolean {
function extractSenderId(msg: MessageInRow, content: any): string | null {
const raw: string | null = content?.senderId || content?.author?.userId || null;
if (!raw) return null;
// Already namespaced (e.g. "telegram:123") — use as-is.
if (raw.includes(':')) return raw;
// Raw platform id from chat-sdk serialization — prefix with channel type.
if (!msg.channel_type) return raw;
// Already namespaced for this channel — use as-is. `includes(':')` would be
// wrong here: Teams Bot Framework user ids like `29:1abc...` natively
// contain a colon and would slip through unprefixed.
if (raw.startsWith(`${msg.channel_type}:`)) return raw;
return `${msg.channel_type}:${raw}`;
}

Expand Down
16 changes: 10 additions & 6 deletions src/modules/permissions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ function extractAndUpsertUser(event: InboundEvent): string | null {
const rawHandle = senderIdField ?? senderField ?? authorUserId;
if (!rawHandle) return null;

const userId = rawHandle.includes(':') ? rawHandle : `${event.channelType}:${rawHandle}`;
// Namespace the handle as `<channel>:<handle>` unless it's already namespaced
// for this channel. `includes(':')` is wrong: Teams Bot Framework user ids
// like `29:1abc...` natively contain a colon, so that check would skip the
// prefix and produce `29:1abc...` instead of `teams:29:1abc...`.
const userId = rawHandle.startsWith(`${event.channelType}:`) ? rawHandle : `${event.channelType}:${rawHandle}`;
if (!getUser(userId)) {
upsertUser({
id: userId,
Expand Down Expand Up @@ -227,11 +231,11 @@ async function handleSenderApprovalResponse(payload: ResponsePayload): Promise<b
if (!row) return false;

// payload.userId is the raw platform userId (e.g. "6037840640"); namespace it
// with the channel type so it matches users(id) format. Some platforms
// (e.g. Teams "29:xxx") already include a colon — mirror resolveOrCreateUser
// logic and only prefix when the raw id has no colon.
// with the channel type so it matches users(id) format. Teams ids like
// "29:xxx" natively contain a colon, so we can't use `includes(':')` as the
// "already namespaced" check — match by channel-type prefix instead.
const clickerId = payload.userId
? payload.userId.includes(':')
? payload.userId.startsWith(`${payload.channelType}:`)
? payload.userId
: `${payload.channelType}:${payload.userId}`
: null;
Expand Down Expand Up @@ -312,7 +316,7 @@ async function handleChannelApprovalResponse(payload: ResponsePayload): Promise<
if (!row) return false;

const clickerId = payload.userId
? payload.userId.includes(':')
? payload.userId.startsWith(`${payload.channelType}:`)
? payload.userId
: `${payload.channelType}:${payload.userId}`
: null;
Expand Down
10 changes: 5 additions & 5 deletions src/modules/permissions/sender-approval.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function stranger(text: string) {
id: `stranger-${Math.random().toString(36).slice(2, 8)}`,
kind: 'chat' as const,
content: JSON.stringify({
senderId: 'tg:stranger',
senderId: 'stranger-tg-id',
senderName: 'Stranger',
text,
}),
Expand Down Expand Up @@ -220,7 +220,7 @@ describe('unknown-sender request_approval flow', () => {
// Member row added for the stranger against the wired agent group.
const member = getDb()
.prepare('SELECT 1 AS x FROM agent_group_members WHERE user_id = ? AND agent_group_id = ?')
.get('tg:stranger', 'ag-1');
.get('telegram:stranger-tg-id', 'ag-1');
expect(member).toBeDefined();

// Pending row cleared.
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('unknown-sender request_approval flow', () => {
expect(count).toBe(0);
const member = getDb()
.prepare('SELECT 1 AS x FROM agent_group_members WHERE user_id = ? AND agent_group_id = ?')
.get('tg:stranger', 'ag-1');
.get('telegram:stranger-tg-id', 'ag-1');
expect(member).toBeUndefined();
});

Expand Down Expand Up @@ -292,7 +292,7 @@ describe('unknown-sender request_approval flow', () => {
// No member added for the stranger.
const member = getDb()
.prepare('SELECT 1 AS x FROM agent_group_members WHERE user_id = ? AND agent_group_id = ?')
.get('tg:stranger', 'ag-1');
.get('telegram:stranger-tg-id', 'ag-1');
expect(member).toBeUndefined();

// Pending row is still there — a legitimate approver can still act on it.
Expand Down Expand Up @@ -338,7 +338,7 @@ describe('unknown-sender request_approval flow', () => {
// Stranger admitted thanks to the admin's authority.
const member = getDb()
.prepare('SELECT 1 AS x FROM agent_group_members WHERE user_id = ? AND agent_group_id = ?')
.get('tg:stranger', 'ag-1');
.get('telegram:stranger-tg-id', 'ag-1');
expect(member).toBeDefined();
});
});
Loading