@kodelyth/msteams 2026.5.42 → 2026.6.2
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/klaw.plugin.json +726 -2
- package/package.json +18 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -2
- package/config-api.ts +0 -4
- package/contract-api.ts +0 -4
- package/index.ts +0 -20
- package/runtime-api.ts +0 -66
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/ai-entity.ts +0 -7
- package/src/approval-auth.ts +0 -44
- package/src/attachments/bot-framework.test.ts +0 -506
- package/src/attachments/bot-framework.ts +0 -348
- package/src/attachments/download.ts +0 -328
- package/src/attachments/graph.test.ts +0 -441
- package/src/attachments/graph.ts +0 -489
- package/src/attachments/html.ts +0 -122
- package/src/attachments/payload.ts +0 -14
- package/src/attachments/remote-media.test.ts +0 -187
- package/src/attachments/remote-media.ts +0 -86
- package/src/attachments/shared.test.ts +0 -547
- package/src/attachments/shared.ts +0 -655
- package/src/attachments/types.ts +0 -47
- package/src/attachments.graph.test.ts +0 -414
- package/src/attachments.helpers.test.ts +0 -245
- package/src/attachments.test-helpers.ts +0 -17
- package/src/attachments.test.ts +0 -754
- package/src/attachments.ts +0 -18
- package/src/block-streaming-config.test.ts +0 -61
- package/src/channel-api.ts +0 -1
- package/src/channel.actions.test.ts +0 -797
- package/src/channel.directory.test.ts +0 -176
- package/src/channel.message-adapter.test.ts +0 -227
- package/src/channel.runtime.ts +0 -56
- package/src/channel.setup.ts +0 -77
- package/src/channel.test.ts +0 -136
- package/src/channel.ts +0 -1176
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -40
- package/src/conversation-store-fs.test.ts +0 -81
- package/src/conversation-store-fs.ts +0 -149
- package/src/conversation-store-helpers.test.ts +0 -202
- package/src/conversation-store-helpers.ts +0 -105
- package/src/conversation-store-memory.ts +0 -51
- package/src/conversation-store.shared.test.ts +0 -260
- package/src/conversation-store.ts +0 -71
- package/src/directory-live.test.ts +0 -156
- package/src/directory-live.ts +0 -111
- package/src/doctor.ts +0 -27
- package/src/errors.test.ts +0 -154
- package/src/errors.ts +0 -270
- package/src/feedback-reflection-prompt.ts +0 -117
- package/src/feedback-reflection-store.ts +0 -113
- package/src/feedback-reflection.test.ts +0 -237
- package/src/feedback-reflection.ts +0 -268
- package/src/file-consent-helpers.test.ts +0 -328
- package/src/file-consent-helpers.ts +0 -115
- package/src/file-consent-invoke.ts +0 -150
- package/src/file-consent.test.ts +0 -378
- package/src/file-consent.ts +0 -223
- package/src/graph-chat.ts +0 -36
- package/src/graph-group-management.test.ts +0 -332
- package/src/graph-group-management.ts +0 -168
- package/src/graph-members.test.ts +0 -89
- package/src/graph-members.ts +0 -48
- package/src/graph-messages.actions.test.ts +0 -253
- package/src/graph-messages.read.test.ts +0 -391
- package/src/graph-messages.search.test.ts +0 -227
- package/src/graph-messages.test-helpers.ts +0 -50
- package/src/graph-messages.ts +0 -534
- package/src/graph-teams.test.ts +0 -222
- package/src/graph-teams.ts +0 -114
- package/src/graph-thread.test.ts +0 -252
- package/src/graph-thread.ts +0 -146
- package/src/graph-upload.test.ts +0 -253
- package/src/graph-upload.ts +0 -531
- package/src/graph-users.ts +0 -29
- package/src/graph.test.ts +0 -540
- package/src/graph.ts +0 -308
- package/src/inbound.test.ts +0 -221
- package/src/inbound.ts +0 -148
- package/src/index.ts +0 -4
- package/src/media-helpers.test.ts +0 -220
- package/src/media-helpers.ts +0 -105
- package/src/mentions.test.ts +0 -254
- package/src/mentions.ts +0 -114
- package/src/messenger.test.ts +0 -961
- package/src/messenger.ts +0 -608
- package/src/monitor-handler/access.ts +0 -136
- package/src/monitor-handler/inbound-media.test.ts +0 -314
- package/src/monitor-handler/inbound-media.ts +0 -180
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +0 -28
- package/src/monitor-handler/message-handler.authz.test.ts +0 -739
- package/src/monitor-handler/message-handler.dm-media.test.ts +0 -54
- package/src/monitor-handler/message-handler.test-support.ts +0 -99
- package/src/monitor-handler/message-handler.thread-parent.test.ts +0 -225
- package/src/monitor-handler/message-handler.thread-session.test.ts +0 -132
- package/src/monitor-handler/message-handler.ts +0 -1003
- package/src/monitor-handler/reaction-handler.test.ts +0 -325
- package/src/monitor-handler/reaction-handler.ts +0 -122
- package/src/monitor-handler/thread-session.ts +0 -30
- package/src/monitor-handler.adaptive-card.test.ts +0 -158
- package/src/monitor-handler.feedback-authz.test.ts +0 -357
- package/src/monitor-handler.file-consent.test.ts +0 -443
- package/src/monitor-handler.sso.test.ts +0 -576
- package/src/monitor-handler.test-helpers.ts +0 -181
- package/src/monitor-handler.ts +0 -538
- package/src/monitor-handler.types.ts +0 -27
- package/src/monitor-types.ts +0 -6
- package/src/monitor.lifecycle.test.ts +0 -457
- package/src/monitor.test.ts +0 -119
- package/src/monitor.ts +0 -476
- package/src/oauth.flow.ts +0 -77
- package/src/oauth.shared.ts +0 -37
- package/src/oauth.test.ts +0 -350
- package/src/oauth.token.ts +0 -162
- package/src/oauth.ts +0 -130
- package/src/outbound.test.ts +0 -400
- package/src/outbound.ts +0 -198
- package/src/pending-uploads-fs.test.ts +0 -261
- package/src/pending-uploads-fs.ts +0 -235
- package/src/pending-uploads.test.ts +0 -186
- package/src/pending-uploads.ts +0 -121
- package/src/policy.test.ts +0 -156
- package/src/policy.ts +0 -245
- package/src/polls-store-memory.ts +0 -32
- package/src/polls.test.ts +0 -169
- package/src/polls.ts +0 -312
- package/src/presentation.ts +0 -93
- package/src/probe.test.ts +0 -79
- package/src/probe.ts +0 -132
- package/src/reply-dispatcher.test.ts +0 -543
- package/src/reply-dispatcher.ts +0 -523
- package/src/reply-stream-controller.test.ts +0 -424
- package/src/reply-stream-controller.ts +0 -334
- package/src/resolve-allowlist.test.ts +0 -253
- package/src/resolve-allowlist.ts +0 -309
- package/src/revoked-context.ts +0 -17
- package/src/runtime.ts +0 -12
- package/src/sdk-types.ts +0 -59
- package/src/sdk.test.ts +0 -727
- package/src/sdk.ts +0 -916
- package/src/secret-contract.ts +0 -49
- package/src/secret-input.ts +0 -7
- package/src/send-context.test.ts +0 -93
- package/src/send-context.ts +0 -269
- package/src/send.test.ts +0 -588
- package/src/send.ts +0 -697
- package/src/sent-message-cache.test.ts +0 -106
- package/src/sent-message-cache.ts +0 -174
- package/src/session-route.ts +0 -40
- package/src/setup-core.ts +0 -162
- package/src/setup-surface.test.ts +0 -175
- package/src/setup-surface.ts +0 -319
- package/src/sso-token-store.test.ts +0 -74
- package/src/sso-token-store.ts +0 -166
- package/src/sso.ts +0 -300
- package/src/storage.ts +0 -25
- package/src/store-fs.ts +0 -42
- package/src/streaming-message.test.ts +0 -323
- package/src/streaming-message.ts +0 -327
- package/src/test-runtime.ts +0 -16
- package/src/thread-parent-context.test.ts +0 -224
- package/src/thread-parent-context.ts +0 -159
- package/src/token-response.ts +0 -11
- package/src/token.test.ts +0 -268
- package/src/token.ts +0 -194
- package/src/user-agent.test.ts +0 -121
- package/src/user-agent.ts +0 -53
- package/src/webhook-timeouts.ts +0 -27
- package/src/welcome-card.test.ts +0 -104
- package/src/welcome-card.ts +0 -57
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { setMSTeamsRuntime } from "./runtime.js";
|
|
3
|
-
import {
|
|
4
|
-
clearMSTeamsSentMessageCache,
|
|
5
|
-
recordMSTeamsSentMessage,
|
|
6
|
-
wasMSTeamsMessageSent,
|
|
7
|
-
wasMSTeamsMessageSentWithPersistence,
|
|
8
|
-
} from "./sent-message-cache.js";
|
|
9
|
-
|
|
10
|
-
const TTL_MS = 24 * 60 * 60 * 1000;
|
|
11
|
-
|
|
12
|
-
describe("msteams sent message cache", () => {
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
clearMSTeamsSentMessageCache();
|
|
15
|
-
vi.restoreAllMocks();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("records and resolves sent message ids", () => {
|
|
19
|
-
recordMSTeamsSentMessage("conv-1", "msg-1");
|
|
20
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-1")).toBe(true);
|
|
21
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-2")).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("persists sent message ids when runtime state is available", async () => {
|
|
25
|
-
vi.spyOn(Date, "now").mockReturnValue(1_234_567);
|
|
26
|
-
const register = vi.fn().mockResolvedValue(undefined);
|
|
27
|
-
const lookup = vi.fn().mockResolvedValue({ sentAt: Date.now() });
|
|
28
|
-
const openKeyedStore = vi.fn(() => ({
|
|
29
|
-
register,
|
|
30
|
-
lookup,
|
|
31
|
-
consume: vi.fn(),
|
|
32
|
-
delete: vi.fn(),
|
|
33
|
-
entries: vi.fn(),
|
|
34
|
-
clear: vi.fn(),
|
|
35
|
-
}));
|
|
36
|
-
setMSTeamsRuntime({
|
|
37
|
-
state: { openKeyedStore },
|
|
38
|
-
logging: { getChildLogger: () => ({ warn: vi.fn() }) },
|
|
39
|
-
} as never);
|
|
40
|
-
|
|
41
|
-
recordMSTeamsSentMessage("conv-1", "msg-2");
|
|
42
|
-
|
|
43
|
-
await vi.waitFor(() => expect(register).toHaveBeenCalledTimes(1));
|
|
44
|
-
expect(register).toHaveBeenCalledWith("conv-1:msg-2", { sentAt: 1_234_567 });
|
|
45
|
-
|
|
46
|
-
clearMSTeamsSentMessageCache();
|
|
47
|
-
await expect(
|
|
48
|
-
wasMSTeamsMessageSentWithPersistence({ conversationId: "conv-1", messageId: "msg-2" }),
|
|
49
|
-
).resolves.toBe(true);
|
|
50
|
-
expect(openKeyedStore).toHaveBeenCalledTimes(2);
|
|
51
|
-
expect(lookup).toHaveBeenCalledWith("conv-1:msg-2");
|
|
52
|
-
|
|
53
|
-
lookup.mockClear();
|
|
54
|
-
await expect(
|
|
55
|
-
wasMSTeamsMessageSentWithPersistence({ conversationId: "conv-1", messageId: "msg-2" }),
|
|
56
|
-
).resolves.toBe(true);
|
|
57
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-2")).toBe(true);
|
|
58
|
-
expect(lookup).not.toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("preserves the original TTL when recovering sent-message ids from persistent state", async () => {
|
|
62
|
-
const sentAt = 1_000_000;
|
|
63
|
-
const lookup = vi.fn().mockResolvedValue({ sentAt });
|
|
64
|
-
const openKeyedStore = vi.fn(() => ({
|
|
65
|
-
register: vi.fn(),
|
|
66
|
-
lookup,
|
|
67
|
-
consume: vi.fn(),
|
|
68
|
-
delete: vi.fn(),
|
|
69
|
-
entries: vi.fn(),
|
|
70
|
-
clear: vi.fn(),
|
|
71
|
-
}));
|
|
72
|
-
setMSTeamsRuntime({
|
|
73
|
-
state: { openKeyedStore },
|
|
74
|
-
logging: { getChildLogger: () => ({ warn: vi.fn() }) },
|
|
75
|
-
} as never);
|
|
76
|
-
|
|
77
|
-
vi.spyOn(Date, "now").mockReturnValue(sentAt + TTL_MS - 1);
|
|
78
|
-
await expect(
|
|
79
|
-
wasMSTeamsMessageSentWithPersistence({ conversationId: "conv-1", messageId: "msg-4" }),
|
|
80
|
-
).resolves.toBe(true);
|
|
81
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-4")).toBe(true);
|
|
82
|
-
|
|
83
|
-
lookup.mockClear();
|
|
84
|
-
vi.mocked(Date.now).mockReturnValue(sentAt + TTL_MS + 1);
|
|
85
|
-
|
|
86
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-4")).toBe(false);
|
|
87
|
-
expect(lookup).not.toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("falls back to in-memory sent-message markers when persistent state cannot open", () => {
|
|
91
|
-
const warn = vi.fn();
|
|
92
|
-
setMSTeamsRuntime({
|
|
93
|
-
state: {
|
|
94
|
-
openKeyedStore: vi.fn(() => {
|
|
95
|
-
throw new Error("sqlite unavailable");
|
|
96
|
-
}),
|
|
97
|
-
},
|
|
98
|
-
logging: { getChildLogger: () => ({ warn }) },
|
|
99
|
-
} as never);
|
|
100
|
-
|
|
101
|
-
recordMSTeamsSentMessage("conv-1", "msg-3");
|
|
102
|
-
|
|
103
|
-
expect(wasMSTeamsMessageSent("conv-1", "msg-3")).toBe(true);
|
|
104
|
-
expect(warn).toHaveBeenCalled();
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { getOptionalMSTeamsRuntime } from "./runtime.js";
|
|
2
|
-
|
|
3
|
-
const TTL_MS = 24 * 60 * 60 * 1000;
|
|
4
|
-
const PERSISTENT_MAX_ENTRIES = 1000;
|
|
5
|
-
const PERSISTENT_NAMESPACE = "msteams.sent-messages";
|
|
6
|
-
const MSTEAMS_SENT_MESSAGES_KEY = Symbol.for("klaw.msteamsSentMessages");
|
|
7
|
-
|
|
8
|
-
type MSTeamsSentMessageRecord = {
|
|
9
|
-
sentAt: number;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
type MSTeamsSentMessageStore = {
|
|
13
|
-
register(key: string, value: MSTeamsSentMessageRecord, opts?: { ttlMs?: number }): Promise<void>;
|
|
14
|
-
lookup(key: string): Promise<MSTeamsSentMessageRecord | undefined>;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
let sentMessageCache: Map<string, Map<string, number>> | undefined;
|
|
18
|
-
let persistentStore: MSTeamsSentMessageStore | undefined;
|
|
19
|
-
let persistentStoreDisabled = false;
|
|
20
|
-
|
|
21
|
-
function getSentMessageCache(): Map<string, Map<string, number>> {
|
|
22
|
-
if (!sentMessageCache) {
|
|
23
|
-
const globalStore = globalThis as Record<PropertyKey, unknown>;
|
|
24
|
-
sentMessageCache =
|
|
25
|
-
(globalStore[MSTEAMS_SENT_MESSAGES_KEY] as Map<string, Map<string, number>> | undefined) ??
|
|
26
|
-
new Map<string, Map<string, number>>();
|
|
27
|
-
globalStore[MSTEAMS_SENT_MESSAGES_KEY] = sentMessageCache;
|
|
28
|
-
}
|
|
29
|
-
return sentMessageCache;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function makePersistentKey(conversationId: string, messageId: string): string {
|
|
33
|
-
return `${conversationId}:${messageId}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function reportPersistentSentMessageError(error: unknown): void {
|
|
37
|
-
try {
|
|
38
|
-
getOptionalMSTeamsRuntime()
|
|
39
|
-
?.logging.getChildLogger({ plugin: "msteams", feature: "sent-message-state" })
|
|
40
|
-
.warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
|
|
41
|
-
} catch {
|
|
42
|
-
// Best effort only: persistent state must never break Teams routing.
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function disablePersistentSentMessageStore(error: unknown): void {
|
|
47
|
-
persistentStoreDisabled = true;
|
|
48
|
-
persistentStore = undefined;
|
|
49
|
-
reportPersistentSentMessageError(error);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getPersistentSentMessageStore(): MSTeamsSentMessageStore | undefined {
|
|
53
|
-
if (persistentStoreDisabled) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
if (persistentStore) {
|
|
57
|
-
return persistentStore;
|
|
58
|
-
}
|
|
59
|
-
const runtime = getOptionalMSTeamsRuntime();
|
|
60
|
-
if (!runtime) {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
persistentStore = runtime.state.openKeyedStore<MSTeamsSentMessageRecord>({
|
|
65
|
-
namespace: PERSISTENT_NAMESPACE,
|
|
66
|
-
maxEntries: PERSISTENT_MAX_ENTRIES,
|
|
67
|
-
defaultTtlMs: TTL_MS,
|
|
68
|
-
});
|
|
69
|
-
return persistentStore;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
disablePersistentSentMessageStore(error);
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function cleanupExpired(scopeKey: string, entry: Map<string, number>, now: number): void {
|
|
77
|
-
for (const [id, timestamp] of entry) {
|
|
78
|
-
if (now - timestamp > TTL_MS) {
|
|
79
|
-
entry.delete(id);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (entry.size === 0) {
|
|
83
|
-
getSentMessageCache().delete(scopeKey);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function rememberSentMessageInMemory(
|
|
88
|
-
conversationId: string,
|
|
89
|
-
messageId: string,
|
|
90
|
-
sentAt: number,
|
|
91
|
-
): void {
|
|
92
|
-
const store = getSentMessageCache();
|
|
93
|
-
let entry = store.get(conversationId);
|
|
94
|
-
if (!entry) {
|
|
95
|
-
entry = new Map<string, number>();
|
|
96
|
-
store.set(conversationId, entry);
|
|
97
|
-
}
|
|
98
|
-
entry.set(messageId, sentAt);
|
|
99
|
-
if (entry.size > 200) {
|
|
100
|
-
cleanupExpired(conversationId, entry, sentAt);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function rememberPersistentSentMessage(params: {
|
|
105
|
-
conversationId: string;
|
|
106
|
-
messageId: string;
|
|
107
|
-
sentAt: number;
|
|
108
|
-
}): void {
|
|
109
|
-
const store = getPersistentSentMessageStore();
|
|
110
|
-
if (!store) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
void store
|
|
114
|
-
.register(makePersistentKey(params.conversationId, params.messageId), { sentAt: params.sentAt })
|
|
115
|
-
.catch(disablePersistentSentMessageStore);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function lookupPersistentSentMessage(params: {
|
|
119
|
-
conversationId: string;
|
|
120
|
-
messageId: string;
|
|
121
|
-
}): Promise<number | undefined> {
|
|
122
|
-
const store = getPersistentSentMessageStore();
|
|
123
|
-
if (!store) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
try {
|
|
127
|
-
return (await store.lookup(makePersistentKey(params.conversationId, params.messageId)))?.sentAt;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
disablePersistentSentMessageStore(error);
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function recordMSTeamsSentMessage(conversationId: string, messageId: string): void {
|
|
135
|
-
if (!conversationId || !messageId) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const now = Date.now();
|
|
139
|
-
rememberSentMessageInMemory(conversationId, messageId, now);
|
|
140
|
-
rememberPersistentSentMessage({ conversationId, messageId, sentAt: now });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function wasMSTeamsMessageSent(conversationId: string, messageId: string): boolean {
|
|
144
|
-
const entry = getSentMessageCache().get(conversationId);
|
|
145
|
-
if (!entry) {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
cleanupExpired(conversationId, entry, Date.now());
|
|
149
|
-
return entry.has(messageId);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export async function wasMSTeamsMessageSentWithPersistence(params: {
|
|
153
|
-
conversationId: string;
|
|
154
|
-
messageId: string;
|
|
155
|
-
}): Promise<boolean> {
|
|
156
|
-
if (!params.conversationId || !params.messageId) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
if (wasMSTeamsMessageSent(params.conversationId, params.messageId)) {
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
const sentAt = await lookupPersistentSentMessage(params);
|
|
163
|
-
if (sentAt == null) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
rememberSentMessageInMemory(params.conversationId, params.messageId, sentAt);
|
|
167
|
-
return wasMSTeamsMessageSent(params.conversationId, params.messageId);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function clearMSTeamsSentMessageCache(): void {
|
|
171
|
-
getSentMessageCache().clear();
|
|
172
|
-
persistentStore = undefined;
|
|
173
|
-
persistentStoreDisabled = false;
|
|
174
|
-
}
|
package/src/session-route.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildChannelOutboundSessionRoute,
|
|
3
|
-
stripChannelTargetPrefix,
|
|
4
|
-
stripTargetKindPrefix,
|
|
5
|
-
type ChannelOutboundSessionRouteParams,
|
|
6
|
-
} from "klaw/plugin-sdk/channel-core";
|
|
7
|
-
import { normalizeLowercaseStringOrEmpty } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
8
|
-
|
|
9
|
-
export function resolveMSTeamsOutboundSessionRoute(params: ChannelOutboundSessionRouteParams) {
|
|
10
|
-
let trimmed = stripChannelTargetPrefix(params.target, "msteams", "teams");
|
|
11
|
-
if (!trimmed) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const lower = normalizeLowercaseStringOrEmpty(trimmed);
|
|
16
|
-
const isUser = lower.startsWith("user:");
|
|
17
|
-
const rawId = stripTargetKindPrefix(trimmed);
|
|
18
|
-
if (!rawId) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
const conversationId = rawId.split(";")[0] ?? rawId;
|
|
22
|
-
const isChannel = !isUser && /@thread\.tacv2/i.test(conversationId);
|
|
23
|
-
return buildChannelOutboundSessionRoute({
|
|
24
|
-
cfg: params.cfg,
|
|
25
|
-
agentId: params.agentId,
|
|
26
|
-
channel: "msteams",
|
|
27
|
-
accountId: params.accountId,
|
|
28
|
-
peer: {
|
|
29
|
-
kind: isUser ? "direct" : isChannel ? "channel" : "group",
|
|
30
|
-
id: conversationId,
|
|
31
|
-
},
|
|
32
|
-
chatType: isUser ? "direct" : isChannel ? "channel" : "group",
|
|
33
|
-
from: isUser
|
|
34
|
-
? `msteams:${conversationId}`
|
|
35
|
-
: isChannel
|
|
36
|
-
? `msteams:channel:${conversationId}`
|
|
37
|
-
: `msteams:group:${conversationId}`,
|
|
38
|
-
to: isUser ? `user:${conversationId}` : `conversation:${conversationId}`,
|
|
39
|
-
});
|
|
40
|
-
}
|
package/src/setup-core.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import type { KlawConfig } from "klaw/plugin-sdk/config-contracts";
|
|
2
|
-
import {
|
|
3
|
-
createStandardChannelSetupStatus,
|
|
4
|
-
DEFAULT_ACCOUNT_ID,
|
|
5
|
-
createSetupTranslator,
|
|
6
|
-
type ChannelSetupAdapter,
|
|
7
|
-
type ChannelSetupWizard,
|
|
8
|
-
type WizardPrompter,
|
|
9
|
-
} from "klaw/plugin-sdk/setup";
|
|
10
|
-
import { formatDocsLink } from "klaw/plugin-sdk/setup-tools";
|
|
11
|
-
import { normalizeSecretInputString } from "./secret-input.js";
|
|
12
|
-
import { hasConfiguredMSTeamsCredentials, resolveMSTeamsCredentials } from "./token.js";
|
|
13
|
-
|
|
14
|
-
const t = createSetupTranslator();
|
|
15
|
-
|
|
16
|
-
export const msteamsSetupAdapter: ChannelSetupAdapter = {
|
|
17
|
-
resolveAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
18
|
-
applyAccountConfig: ({ cfg }) => ({
|
|
19
|
-
...cfg,
|
|
20
|
-
channels: {
|
|
21
|
-
...cfg.channels,
|
|
22
|
-
msteams: {
|
|
23
|
-
...cfg.channels?.msteams,
|
|
24
|
-
enabled: true,
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
}),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const channel = "msteams" as const;
|
|
31
|
-
|
|
32
|
-
async function promptMSTeamsCredentials(prompter: WizardPrompter): Promise<{
|
|
33
|
-
appId: string;
|
|
34
|
-
appPassword: string;
|
|
35
|
-
tenantId: string;
|
|
36
|
-
}> {
|
|
37
|
-
const appId = (
|
|
38
|
-
await prompter.text({
|
|
39
|
-
message: t("wizard.msteams.appIdPrompt"),
|
|
40
|
-
validate: (value) => (value?.trim() ? undefined : t("common.required")),
|
|
41
|
-
})
|
|
42
|
-
).trim();
|
|
43
|
-
const appPassword = (
|
|
44
|
-
await prompter.text({
|
|
45
|
-
message: t("wizard.msteams.appPasswordPrompt"),
|
|
46
|
-
validate: (value) => (value?.trim() ? undefined : t("common.required")),
|
|
47
|
-
})
|
|
48
|
-
).trim();
|
|
49
|
-
const tenantId = (
|
|
50
|
-
await prompter.text({
|
|
51
|
-
message: t("wizard.msteams.tenantIdPrompt"),
|
|
52
|
-
validate: (value) => (value?.trim() ? undefined : t("common.required")),
|
|
53
|
-
})
|
|
54
|
-
).trim();
|
|
55
|
-
return { appId, appPassword, tenantId };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function noteMSTeamsCredentialHelp(prompter: WizardPrompter): Promise<void> {
|
|
59
|
-
await prompter.note(
|
|
60
|
-
[
|
|
61
|
-
t("wizard.msteams.helpAzureBot"),
|
|
62
|
-
t("wizard.msteams.helpClientSecret"),
|
|
63
|
-
t("wizard.msteams.helpWebhook"),
|
|
64
|
-
t("wizard.msteams.helpEnvTip"),
|
|
65
|
-
t("wizard.channels.docs", { link: formatDocsLink("/channels/msteams", "msteams") }),
|
|
66
|
-
].join("\n"),
|
|
67
|
-
t("wizard.msteams.credentialsTitle"),
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function createMSTeamsSetupWizardBase(): Pick<
|
|
72
|
-
ChannelSetupWizard,
|
|
73
|
-
| "channel"
|
|
74
|
-
| "resolveAccountIdForConfigure"
|
|
75
|
-
| "resolveShouldPromptAccountIds"
|
|
76
|
-
| "status"
|
|
77
|
-
| "credentials"
|
|
78
|
-
| "finalize"
|
|
79
|
-
> {
|
|
80
|
-
return {
|
|
81
|
-
channel,
|
|
82
|
-
resolveAccountIdForConfigure: () => DEFAULT_ACCOUNT_ID,
|
|
83
|
-
resolveShouldPromptAccountIds: () => false,
|
|
84
|
-
status: createStandardChannelSetupStatus({
|
|
85
|
-
channelLabel: "MS Teams",
|
|
86
|
-
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
87
|
-
unconfiguredLabel: t("wizard.channels.statusNeedsAppCredentials"),
|
|
88
|
-
configuredHint: t("wizard.channels.statusConfigured"),
|
|
89
|
-
unconfiguredHint: t("wizard.channels.statusNeedsAppCreds"),
|
|
90
|
-
configuredScore: 2,
|
|
91
|
-
unconfiguredScore: 0,
|
|
92
|
-
includeStatusLine: true,
|
|
93
|
-
resolveConfigured: ({ cfg }) =>
|
|
94
|
-
Boolean(resolveMSTeamsCredentials(cfg.channels?.msteams)) ||
|
|
95
|
-
hasConfiguredMSTeamsCredentials(cfg.channels?.msteams),
|
|
96
|
-
}),
|
|
97
|
-
credentials: [],
|
|
98
|
-
finalize: async ({ cfg, prompter }) => {
|
|
99
|
-
const resolved = resolveMSTeamsCredentials(cfg.channels?.msteams);
|
|
100
|
-
const hasConfigCreds = hasConfiguredMSTeamsCredentials(cfg.channels?.msteams);
|
|
101
|
-
const canUseEnv = Boolean(
|
|
102
|
-
!hasConfigCreds &&
|
|
103
|
-
normalizeSecretInputString(process.env.MSTEAMS_APP_ID) &&
|
|
104
|
-
normalizeSecretInputString(process.env.MSTEAMS_APP_PASSWORD) &&
|
|
105
|
-
normalizeSecretInputString(process.env.MSTEAMS_TENANT_ID),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
let next: KlawConfig = cfg;
|
|
109
|
-
let appId: string | null = null;
|
|
110
|
-
let appPassword: string | null = null;
|
|
111
|
-
let tenantId: string | null = null;
|
|
112
|
-
|
|
113
|
-
if (!resolved && !hasConfigCreds) {
|
|
114
|
-
await noteMSTeamsCredentialHelp(prompter);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (canUseEnv) {
|
|
118
|
-
const keepEnv = await prompter.confirm({
|
|
119
|
-
message: t("wizard.msteams.envPrompt"),
|
|
120
|
-
initialValue: true,
|
|
121
|
-
});
|
|
122
|
-
if (keepEnv) {
|
|
123
|
-
next = msteamsSetupAdapter.applyAccountConfig({
|
|
124
|
-
cfg: next,
|
|
125
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
126
|
-
input: {},
|
|
127
|
-
});
|
|
128
|
-
} else {
|
|
129
|
-
({ appId, appPassword, tenantId } = await promptMSTeamsCredentials(prompter));
|
|
130
|
-
}
|
|
131
|
-
} else if (hasConfigCreds) {
|
|
132
|
-
const keep = await prompter.confirm({
|
|
133
|
-
message: t("wizard.msteams.credentialsKeep"),
|
|
134
|
-
initialValue: true,
|
|
135
|
-
});
|
|
136
|
-
if (!keep) {
|
|
137
|
-
({ appId, appPassword, tenantId } = await promptMSTeamsCredentials(prompter));
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
({ appId, appPassword, tenantId } = await promptMSTeamsCredentials(prompter));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (appId && appPassword && tenantId) {
|
|
144
|
-
next = {
|
|
145
|
-
...next,
|
|
146
|
-
channels: {
|
|
147
|
-
...next.channels,
|
|
148
|
-
msteams: {
|
|
149
|
-
...next.channels?.msteams,
|
|
150
|
-
enabled: true,
|
|
151
|
-
appId,
|
|
152
|
-
appPassword,
|
|
153
|
-
tenantId,
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return { cfg: next, accountId: DEFAULT_ACCOUNT_ID };
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_ACCOUNT_ID } from "klaw/plugin-sdk/setup";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { createMSTeamsSetupWizardBase, msteamsSetupAdapter } from "./setup-core.js";
|
|
4
|
-
|
|
5
|
-
const resolveMSTeamsUserAllowlist = vi.hoisted(() => vi.fn());
|
|
6
|
-
const resolveMSTeamsChannelAllowlist = vi.hoisted(() => vi.fn());
|
|
7
|
-
const normalizeSecretInputString = vi.hoisted(() =>
|
|
8
|
-
vi.fn((value: unknown) => (typeof value === "string" ? value.trim() || undefined : undefined)),
|
|
9
|
-
);
|
|
10
|
-
const hasConfiguredMSTeamsCredentials = vi.hoisted(() => vi.fn());
|
|
11
|
-
const resolveMSTeamsCredentials = vi.hoisted(() => vi.fn());
|
|
12
|
-
|
|
13
|
-
vi.mock("./resolve-allowlist.js", () => ({
|
|
14
|
-
parseMSTeamsTeamEntry: vi.fn(),
|
|
15
|
-
resolveMSTeamsChannelAllowlist,
|
|
16
|
-
resolveMSTeamsUserAllowlist,
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
vi.mock("./secret-input.js", () => ({
|
|
20
|
-
normalizeSecretInputString,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
vi.mock("./token.js", () => ({
|
|
24
|
-
hasConfiguredMSTeamsCredentials,
|
|
25
|
-
resolveMSTeamsCredentials,
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
describe("msteams setup surface", () => {
|
|
29
|
-
const msteamsSetupWizard = createMSTeamsSetupWizardBase();
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
resolveMSTeamsUserAllowlist.mockReset();
|
|
33
|
-
resolveMSTeamsChannelAllowlist.mockReset();
|
|
34
|
-
normalizeSecretInputString.mockClear();
|
|
35
|
-
hasConfiguredMSTeamsCredentials.mockReset();
|
|
36
|
-
resolveMSTeamsCredentials.mockReset();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
vi.unstubAllEnvs();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("always resolves to the default account", () => {
|
|
44
|
-
expect(msteamsSetupAdapter.resolveAccountId?.({ accountId: "work" } as never)).toBe(
|
|
45
|
-
DEFAULT_ACCOUNT_ID,
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("enables the msteams channel without dropping existing config", () => {
|
|
50
|
-
expect(
|
|
51
|
-
msteamsSetupAdapter.applyAccountConfig?.({
|
|
52
|
-
cfg: {
|
|
53
|
-
channels: {
|
|
54
|
-
msteams: {
|
|
55
|
-
appId: "existing-app",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
60
|
-
input: {},
|
|
61
|
-
} as never),
|
|
62
|
-
).toEqual({
|
|
63
|
-
channels: {
|
|
64
|
-
msteams: {
|
|
65
|
-
appId: "existing-app",
|
|
66
|
-
enabled: true,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("reports configured status from resolved credentials", () => {
|
|
73
|
-
resolveMSTeamsCredentials.mockReturnValue({
|
|
74
|
-
appId: "app",
|
|
75
|
-
});
|
|
76
|
-
hasConfiguredMSTeamsCredentials.mockReturnValue(false);
|
|
77
|
-
|
|
78
|
-
expect(
|
|
79
|
-
msteamsSetupWizard.status.resolveConfigured({
|
|
80
|
-
cfg: { channels: { msteams: {} } },
|
|
81
|
-
} as never),
|
|
82
|
-
).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("reports configured status from configured credentials and renders status lines", async () => {
|
|
86
|
-
resolveMSTeamsCredentials.mockReturnValue(null);
|
|
87
|
-
hasConfiguredMSTeamsCredentials.mockReturnValue(true);
|
|
88
|
-
|
|
89
|
-
expect(
|
|
90
|
-
msteamsSetupWizard.status.resolveConfigured({
|
|
91
|
-
cfg: { channels: { msteams: {} } },
|
|
92
|
-
} as never),
|
|
93
|
-
).toBe(true);
|
|
94
|
-
|
|
95
|
-
hasConfiguredMSTeamsCredentials.mockReturnValue(false);
|
|
96
|
-
expect(msteamsSetupWizard.status.resolveStatusLines).toBeTypeOf("function");
|
|
97
|
-
await expect(
|
|
98
|
-
msteamsSetupWizard.status.resolveStatusLines?.({
|
|
99
|
-
cfg: { channels: { msteams: {} } },
|
|
100
|
-
} as never),
|
|
101
|
-
).resolves.toEqual(["MS Teams: needs app credentials"]);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("finalize keeps env credentials when available and accepted", async () => {
|
|
105
|
-
vi.stubEnv("MSTEAMS_APP_ID", "env-app");
|
|
106
|
-
vi.stubEnv("MSTEAMS_APP_PASSWORD", "env-secret");
|
|
107
|
-
vi.stubEnv("MSTEAMS_TENANT_ID", "env-tenant");
|
|
108
|
-
resolveMSTeamsCredentials.mockReturnValue(null);
|
|
109
|
-
hasConfiguredMSTeamsCredentials.mockReturnValue(false);
|
|
110
|
-
|
|
111
|
-
const result = await msteamsSetupWizard.finalize?.({
|
|
112
|
-
cfg: { channels: { msteams: { existing: true } } },
|
|
113
|
-
prompter: {
|
|
114
|
-
confirm: vi.fn(async () => true),
|
|
115
|
-
note: vi.fn(async () => {}),
|
|
116
|
-
text: vi.fn(),
|
|
117
|
-
},
|
|
118
|
-
} as never);
|
|
119
|
-
|
|
120
|
-
expect(result).toEqual({
|
|
121
|
-
accountId: "default",
|
|
122
|
-
cfg: {
|
|
123
|
-
channels: {
|
|
124
|
-
msteams: {
|
|
125
|
-
existing: true,
|
|
126
|
-
enabled: true,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("finalize prompts for manual credentials when env/config creds are unavailable", async () => {
|
|
134
|
-
resolveMSTeamsCredentials.mockReturnValue(null);
|
|
135
|
-
hasConfiguredMSTeamsCredentials.mockReturnValue(false);
|
|
136
|
-
const note = vi.fn(async () => {});
|
|
137
|
-
const confirm = vi.fn(async () => false);
|
|
138
|
-
const text = vi.fn(async ({ message }: { message: string }) => {
|
|
139
|
-
if (message === "Enter MS Teams App ID") {
|
|
140
|
-
return "app-id";
|
|
141
|
-
}
|
|
142
|
-
if (message === "Enter MS Teams App Password") {
|
|
143
|
-
return "app-password";
|
|
144
|
-
}
|
|
145
|
-
if (message === "Enter MS Teams Tenant ID") {
|
|
146
|
-
return "tenant-id";
|
|
147
|
-
}
|
|
148
|
-
throw new Error(`Unexpected prompt: ${message}`);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const result = await msteamsSetupWizard.finalize?.({
|
|
152
|
-
cfg: { channels: { msteams: {} } },
|
|
153
|
-
prompter: {
|
|
154
|
-
confirm,
|
|
155
|
-
note,
|
|
156
|
-
text,
|
|
157
|
-
},
|
|
158
|
-
} as never);
|
|
159
|
-
|
|
160
|
-
expect(note).toHaveBeenCalled();
|
|
161
|
-
expect(result).toEqual({
|
|
162
|
-
accountId: "default",
|
|
163
|
-
cfg: {
|
|
164
|
-
channels: {
|
|
165
|
-
msteams: {
|
|
166
|
-
enabled: true,
|
|
167
|
-
appId: "app-id",
|
|
168
|
-
appPassword: "app-password",
|
|
169
|
-
tenantId: "tenant-id",
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|