@hahnfeld/teams-adapter 1.1.1 → 1.3.0
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/README.md +81 -28
- package/dist/activity.d.ts +0 -20
- package/dist/activity.d.ts.map +1 -1
- package/dist/adapter.d.ts +15 -24
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +161 -222
- package/dist/adapter.js.map +1 -1
- package/dist/assistant.d.ts +0 -1
- package/dist/assistant.d.ts.map +1 -1
- package/dist/assistant.js +0 -3
- package/dist/assistant.js.map +1 -1
- package/dist/commands/agents.d.ts +0 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +0 -3
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +0 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/integrate.d.ts +0 -1
- package/dist/commands/integrate.d.ts.map +1 -1
- package/dist/commands/integrate.js +0 -3
- package/dist/commands/integrate.js.map +1 -1
- package/dist/commands/menu.d.ts +0 -1
- package/dist/commands/menu.d.ts.map +1 -1
- package/dist/commands/menu.js +0 -3
- package/dist/commands/menu.js.map +1 -1
- package/dist/commands/new-session.d.ts +0 -1
- package/dist/commands/new-session.d.ts.map +1 -1
- package/dist/commands/new-session.js +0 -3
- package/dist/commands/new-session.js.map +1 -1
- package/dist/commands/session.d.ts +0 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +0 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/settings.d.ts +0 -1
- package/dist/commands/settings.d.ts.map +1 -1
- package/dist/commands/settings.js +0 -3
- package/dist/commands/settings.js.map +1 -1
- package/dist/formatting.d.ts +0 -62
- package/dist/formatting.d.ts.map +1 -1
- package/dist/formatting.js +0 -190
- package/dist/formatting.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/message-composer.d.ts +94 -0
- package/dist/message-composer.d.ts.map +1 -0
- package/dist/message-composer.js +331 -0
- package/dist/message-composer.js.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +33 -6
- package/dist/plugin.js.map +1 -1
- package/dist/rate-limiter.d.ts +17 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +201 -0
- package/dist/rate-limiter.js.map +1 -0
- package/package.json +2 -2
- package/dist/draft-manager.d.ts +0 -56
- package/dist/draft-manager.d.ts.map +0 -1
- package/dist/draft-manager.js +0 -268
- package/dist/draft-manager.js.map +0 -1
- package/dist/renderer.d.ts +0 -49
- package/dist/renderer.d.ts.map +0 -1
- package/dist/renderer.js +0 -55
- package/dist/renderer.js.map +0 -1
- package/dist/task-modules.d.ts +0 -34
- package/dist/task-modules.d.ts.map +0 -1
- package/dist/task-modules.js +0 -136
- package/dist/task-modules.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-conversation rate limiter for Teams Bot Framework API.
|
|
3
|
+
*
|
|
4
|
+
* Teams enforces rate limits per bot per conversation thread:
|
|
5
|
+
* 7 ops/1s, 8 ops/2s, 60 ops/30s, 1800 ops/3600s
|
|
6
|
+
*
|
|
7
|
+
* This limiter tracks sliding windows and queues operations when any
|
|
8
|
+
* window would be exceeded. Supports coalescing — queued operations
|
|
9
|
+
* with the same key replace earlier ones (e.g., rapid message edits
|
|
10
|
+
* collapse into a single PUT).
|
|
11
|
+
*/
|
|
12
|
+
import { log } from "@openacp/plugin-sdk";
|
|
13
|
+
/** Our targets — slightly under Teams limits to leave headroom. */
|
|
14
|
+
const RATE_WINDOWS = [
|
|
15
|
+
{ windowMs: 1_000, max: 6 },
|
|
16
|
+
{ windowMs: 2_000, max: 7 },
|
|
17
|
+
{ windowMs: 30_000, max: 55 },
|
|
18
|
+
{ windowMs: 3_600_000, max: 1_700 },
|
|
19
|
+
];
|
|
20
|
+
/** Per-conversation queue state. */
|
|
21
|
+
class ConversationQueue {
|
|
22
|
+
/** Timestamps of completed operations (for sliding window tracking). */
|
|
23
|
+
timestamps = [];
|
|
24
|
+
queue = [];
|
|
25
|
+
draining = false;
|
|
26
|
+
destroyed = false;
|
|
27
|
+
pausedUntil = 0;
|
|
28
|
+
drainTimer;
|
|
29
|
+
/** Resolves the inner drain-wait promise on destroy, preventing coroutine leak. */
|
|
30
|
+
drainWaitResolve;
|
|
31
|
+
/** Enqueue an operation. If key matches a pending op, replace it (coalescing). */
|
|
32
|
+
enqueue(fn, key) {
|
|
33
|
+
if (this.destroyed)
|
|
34
|
+
return Promise.resolve(undefined);
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
if (key) {
|
|
37
|
+
const idx = this.queue.findIndex((op) => op.key === key);
|
|
38
|
+
if (idx !== -1) {
|
|
39
|
+
// Coalesce: resolve the old op as undefined (skipped), replace with new
|
|
40
|
+
this.queue[idx].resolve(undefined);
|
|
41
|
+
this.queue[idx] = { fn, key, resolve: resolve, reject };
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this.queue.push({ fn, key, resolve: resolve, reject });
|
|
46
|
+
this.scheduleDrain();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
get pending() {
|
|
50
|
+
return this.queue.length;
|
|
51
|
+
}
|
|
52
|
+
destroy() {
|
|
53
|
+
this.destroyed = true;
|
|
54
|
+
if (this.drainTimer) {
|
|
55
|
+
clearTimeout(this.drainTimer);
|
|
56
|
+
this.drainTimer = undefined;
|
|
57
|
+
}
|
|
58
|
+
// Unblock any suspended drain-wait so the coroutine can exit
|
|
59
|
+
if (this.drainWaitResolve) {
|
|
60
|
+
this.drainWaitResolve();
|
|
61
|
+
this.drainWaitResolve = undefined;
|
|
62
|
+
}
|
|
63
|
+
// Resolve all pending as undefined
|
|
64
|
+
for (const op of this.queue)
|
|
65
|
+
op.resolve(undefined);
|
|
66
|
+
this.queue.length = 0;
|
|
67
|
+
}
|
|
68
|
+
scheduleDrain() {
|
|
69
|
+
if (this.draining)
|
|
70
|
+
return;
|
|
71
|
+
if (this.drainTimer)
|
|
72
|
+
return;
|
|
73
|
+
const delay = this.getDelay();
|
|
74
|
+
this.drainTimer = setTimeout(() => {
|
|
75
|
+
this.drainTimer = undefined;
|
|
76
|
+
this.drain();
|
|
77
|
+
}, delay);
|
|
78
|
+
}
|
|
79
|
+
/** Calculate how long to wait before next op is allowed. */
|
|
80
|
+
getDelay() {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
// Respect 429 pause
|
|
83
|
+
if (now < this.pausedUntil) {
|
|
84
|
+
return this.pausedUntil - now;
|
|
85
|
+
}
|
|
86
|
+
let maxDelay = 0;
|
|
87
|
+
for (const { windowMs, max } of RATE_WINDOWS) {
|
|
88
|
+
const cutoff = now - windowMs;
|
|
89
|
+
// Timestamps are in ascending order — find the first one in the window
|
|
90
|
+
const firstIdx = this.timestamps.findIndex((t) => t > cutoff);
|
|
91
|
+
if (firstIdx === -1)
|
|
92
|
+
continue;
|
|
93
|
+
const opsInWindow = this.timestamps.length - firstIdx;
|
|
94
|
+
if (opsInWindow >= max) {
|
|
95
|
+
const oldest = this.timestamps[firstIdx];
|
|
96
|
+
const wait = oldest + windowMs - now + 1;
|
|
97
|
+
maxDelay = Math.max(maxDelay, wait);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return maxDelay;
|
|
101
|
+
}
|
|
102
|
+
async drain() {
|
|
103
|
+
if (this.draining)
|
|
104
|
+
return;
|
|
105
|
+
this.draining = true;
|
|
106
|
+
try {
|
|
107
|
+
while (this.queue.length > 0 && !this.destroyed) {
|
|
108
|
+
const delay = this.getDelay();
|
|
109
|
+
if (delay > 0) {
|
|
110
|
+
await new Promise((r) => {
|
|
111
|
+
this.drainWaitResolve = r;
|
|
112
|
+
this.drainTimer = setTimeout(() => {
|
|
113
|
+
this.drainTimer = undefined;
|
|
114
|
+
this.drainWaitResolve = undefined;
|
|
115
|
+
r();
|
|
116
|
+
}, delay);
|
|
117
|
+
});
|
|
118
|
+
// Check if destroyed while waiting
|
|
119
|
+
if (this.destroyed)
|
|
120
|
+
break;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const op = this.queue.shift();
|
|
124
|
+
try {
|
|
125
|
+
const result = await op.fn();
|
|
126
|
+
// Record timestamp only on success — 429s should not consume quota
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
this.timestamps.push(now);
|
|
129
|
+
this.pruneTimestamps(now);
|
|
130
|
+
op.resolve(result);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
const statusCode = err?.statusCode;
|
|
134
|
+
if (statusCode === 429) {
|
|
135
|
+
// Do NOT record a timestamp — the call was rejected
|
|
136
|
+
const retryAfterRaw = err?.headers?.["retry-after"];
|
|
137
|
+
const retryAfterSec = retryAfterRaw ? parseInt(retryAfterRaw, 10) : NaN;
|
|
138
|
+
const retryMs = !isNaN(retryAfterSec) && retryAfterSec > 0
|
|
139
|
+
? retryAfterSec * 1000
|
|
140
|
+
: 2000;
|
|
141
|
+
log.warn({ retryMs }, "[RateLimiter] 429 received, pausing");
|
|
142
|
+
this.pausedUntil = Date.now() + retryMs;
|
|
143
|
+
// Re-queue the failed op at the front
|
|
144
|
+
this.queue.unshift(op);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
// Non-429 errors still consumed a slot
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
this.timestamps.push(now);
|
|
150
|
+
this.pruneTimestamps(now);
|
|
151
|
+
op.reject(err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
this.draining = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Remove timestamps older than the largest window. */
|
|
160
|
+
pruneTimestamps(now) {
|
|
161
|
+
const maxWindow = RATE_WINDOWS[RATE_WINDOWS.length - 1].windowMs;
|
|
162
|
+
const cutoff = now - maxWindow;
|
|
163
|
+
while (this.timestamps.length > 0 && this.timestamps[0] <= cutoff) {
|
|
164
|
+
this.timestamps.shift();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Manages per-conversation rate-limited queues.
|
|
170
|
+
*
|
|
171
|
+
* Usage:
|
|
172
|
+
* const limiter = new ConversationRateLimiter();
|
|
173
|
+
* await limiter.enqueue(conversationId, () => sendText(ctx, text), "main-msg");
|
|
174
|
+
*/
|
|
175
|
+
export class ConversationRateLimiter {
|
|
176
|
+
queues = new Map();
|
|
177
|
+
/** Enqueue an operation for a conversation. Key enables coalescing. */
|
|
178
|
+
enqueue(conversationId, fn, key) {
|
|
179
|
+
let queue = this.queues.get(conversationId);
|
|
180
|
+
if (!queue) {
|
|
181
|
+
queue = new ConversationQueue();
|
|
182
|
+
this.queues.set(conversationId, queue);
|
|
183
|
+
}
|
|
184
|
+
return queue.enqueue(fn, key);
|
|
185
|
+
}
|
|
186
|
+
/** Clean up a conversation's queue (e.g., on session end). */
|
|
187
|
+
cleanup(conversationId) {
|
|
188
|
+
const queue = this.queues.get(conversationId);
|
|
189
|
+
if (queue) {
|
|
190
|
+
queue.destroy();
|
|
191
|
+
this.queues.delete(conversationId);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Clean up all queues. */
|
|
195
|
+
destroy() {
|
|
196
|
+
for (const queue of this.queues.values())
|
|
197
|
+
queue.destroy();
|
|
198
|
+
this.queues.clear();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnfeld/teams-adapter",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Teams adapter plugin for OpenACP —
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Teams adapter plugin for OpenACP — streaming message composer, rate limiter, commands",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
package/dist/draft-manager.d.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { TurnContext } from "@microsoft/agents-hosting";
|
|
2
|
-
export interface MessageRef {
|
|
3
|
-
activityId?: string;
|
|
4
|
-
conversationId?: string;
|
|
5
|
-
serviceUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
/** Function signature for acquiring a bot token — injected from the adapter */
|
|
8
|
-
export type AcquireBotToken = () => Promise<string | null>;
|
|
9
|
-
export declare class TeamsMessageDraft {
|
|
10
|
-
private context;
|
|
11
|
-
private sendQueue;
|
|
12
|
-
private sessionId;
|
|
13
|
-
private acquireBotToken?;
|
|
14
|
-
private buffer;
|
|
15
|
-
private ref?;
|
|
16
|
-
private flushTimer?;
|
|
17
|
-
private flushPromise;
|
|
18
|
-
private lastSentBuffer;
|
|
19
|
-
private firstFlushPending;
|
|
20
|
-
private finalizing;
|
|
21
|
-
constructor(context: TurnContext, sendQueue: {
|
|
22
|
-
enqueue<T>(fn: () => Promise<T>, opts?: {
|
|
23
|
-
type?: string;
|
|
24
|
-
}): Promise<T | undefined>;
|
|
25
|
-
}, sessionId: string, acquireBotToken?: AcquireBotToken | undefined);
|
|
26
|
-
/** Update the TurnContext to the latest inbound turn (prevents stale context refs) */
|
|
27
|
-
updateContext(context: TurnContext): void;
|
|
28
|
-
append(text: string): void;
|
|
29
|
-
getBuffer(): string;
|
|
30
|
-
private scheduleFlush;
|
|
31
|
-
flush(): Promise<void>;
|
|
32
|
-
/**
|
|
33
|
-
* Update an existing message via the Bot Framework REST API.
|
|
34
|
-
* Bypasses the teams.apps SDK context which doesn't support updateActivity.
|
|
35
|
-
*/
|
|
36
|
-
private updateActivityViaRest;
|
|
37
|
-
stripPattern(pattern: RegExp): Promise<void>;
|
|
38
|
-
finalize(): Promise<void>;
|
|
39
|
-
private _finalizeInner;
|
|
40
|
-
}
|
|
41
|
-
export declare class TeamsDraftManager {
|
|
42
|
-
private sendQueue;
|
|
43
|
-
private acquireBotToken?;
|
|
44
|
-
private drafts;
|
|
45
|
-
constructor(sendQueue: {
|
|
46
|
-
enqueue<T>(fn: () => Promise<T>, opts?: {
|
|
47
|
-
type?: string;
|
|
48
|
-
}): Promise<T | undefined>;
|
|
49
|
-
}, acquireBotToken?: AcquireBotToken | undefined);
|
|
50
|
-
getOrCreate(sessionId: string, context: TurnContext): TeamsMessageDraft;
|
|
51
|
-
hasDraft(sessionId: string): boolean;
|
|
52
|
-
getDraft(sessionId: string): TeamsMessageDraft | undefined;
|
|
53
|
-
finalize(sessionId: string, _context?: TurnContext, _isAssistant?: boolean): Promise<void>;
|
|
54
|
-
cleanup(sessionId: string): void;
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=draft-manager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"draft-manager.d.ts","sourceRoot":"","sources":["../src/draft-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAW7D,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE3D,qBAAa,iBAAiB;IAU1B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,eAAe,CAAC;IAZ1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,GAAG,CAAC,CAAa;IACzB,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,UAAU,CAAS;gBAGjB,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE;QAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;KAAE,EACjG,SAAS,EAAE,MAAM,EACjB,eAAe,CAAC,EAAE,eAAe,YAAA;IAG3C,sFAAsF;IACtF,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM1B,SAAS,IAAI,MAAM;IAInB,OAAO,CAAC,aAAa;IASf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkF5B;;;OAGG;YACW,qBAAqB;IA8B7B,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAUjB,cAAc;CAsD7B;AAED,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,eAAe,CAAC;IAJ1B,OAAO,CAAC,MAAM,CAAwC;gBAG5C,SAAS,EAAE;QAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;KAAE,EACjG,eAAe,CAAC,EAAE,eAAe,YAAA;IAG3C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,iBAAiB;IAavE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIpC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIpD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhG,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGjC"}
|
package/dist/draft-manager.js
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { log } from "@openacp/plugin-sdk";
|
|
2
|
-
import { splitMessage } from "./formatting.js";
|
|
3
|
-
import { sendText } from "./send-utils.js";
|
|
4
|
-
/** First flush fires quickly so the user sees content fast */
|
|
5
|
-
const FIRST_FLUSH_INTERVAL = 500;
|
|
6
|
-
/** Subsequent flushes are slower to avoid rate limits (Teams: 7 ops/sec/conversation) */
|
|
7
|
-
const UPDATE_FLUSH_INTERVAL = 2000;
|
|
8
|
-
const MAX_DISPLAY_LENGTH = 25000;
|
|
9
|
-
export class TeamsMessageDraft {
|
|
10
|
-
context;
|
|
11
|
-
sendQueue;
|
|
12
|
-
sessionId;
|
|
13
|
-
acquireBotToken;
|
|
14
|
-
buffer = "";
|
|
15
|
-
ref;
|
|
16
|
-
flushTimer;
|
|
17
|
-
flushPromise = Promise.resolve();
|
|
18
|
-
lastSentBuffer = "";
|
|
19
|
-
firstFlushPending = false;
|
|
20
|
-
finalizing = false;
|
|
21
|
-
constructor(context, sendQueue, sessionId, acquireBotToken) {
|
|
22
|
-
this.context = context;
|
|
23
|
-
this.sendQueue = sendQueue;
|
|
24
|
-
this.sessionId = sessionId;
|
|
25
|
-
this.acquireBotToken = acquireBotToken;
|
|
26
|
-
}
|
|
27
|
-
/** Update the TurnContext to the latest inbound turn (prevents stale context refs) */
|
|
28
|
-
updateContext(context) {
|
|
29
|
-
this.context = context;
|
|
30
|
-
}
|
|
31
|
-
append(text) {
|
|
32
|
-
if (!text)
|
|
33
|
-
return;
|
|
34
|
-
this.buffer += text;
|
|
35
|
-
this.scheduleFlush();
|
|
36
|
-
}
|
|
37
|
-
getBuffer() {
|
|
38
|
-
return this.buffer;
|
|
39
|
-
}
|
|
40
|
-
scheduleFlush() {
|
|
41
|
-
if (this.flushTimer)
|
|
42
|
-
return;
|
|
43
|
-
const interval = this.ref?.activityId ? UPDATE_FLUSH_INTERVAL : FIRST_FLUSH_INTERVAL;
|
|
44
|
-
this.flushTimer = setTimeout(() => {
|
|
45
|
-
this.flushTimer = undefined;
|
|
46
|
-
this.flushPromise = this.flushPromise.then(() => this.flush()).catch(() => { });
|
|
47
|
-
}, interval);
|
|
48
|
-
}
|
|
49
|
-
async flush() {
|
|
50
|
-
if (!this.buffer)
|
|
51
|
-
return;
|
|
52
|
-
if (this.firstFlushPending)
|
|
53
|
-
return;
|
|
54
|
-
// If buffer exceeds limit, finalize the current message with its portion
|
|
55
|
-
// and start a new streaming message for the overflow.
|
|
56
|
-
if (this.buffer.length > MAX_DISPLAY_LENGTH && this.ref?.activityId) {
|
|
57
|
-
const finalChunk = this.buffer.slice(0, MAX_DISPLAY_LENGTH);
|
|
58
|
-
const overflow = this.buffer.slice(MAX_DISPLAY_LENGTH);
|
|
59
|
-
// Update current message with its final content
|
|
60
|
-
await this.updateActivityViaRest(finalChunk).catch(() => { });
|
|
61
|
-
// Reset for a new message
|
|
62
|
-
this.ref = undefined;
|
|
63
|
-
this.lastSentBuffer = "";
|
|
64
|
-
this.firstFlushPending = false;
|
|
65
|
-
this.buffer = overflow;
|
|
66
|
-
// Immediately send the overflow as a new message to resume streaming
|
|
67
|
-
if (overflow) {
|
|
68
|
-
this.firstFlushPending = true;
|
|
69
|
-
try {
|
|
70
|
-
const result = await this.sendQueue.enqueue(() => sendText(this.context, overflow), { type: "other" });
|
|
71
|
-
if (result) {
|
|
72
|
-
this.ref = {
|
|
73
|
-
activityId: result.id,
|
|
74
|
-
conversationId: this.context.activity.conversation?.id,
|
|
75
|
-
serviceUrl: this.context.activity.serviceUrl,
|
|
76
|
-
};
|
|
77
|
-
this.lastSentBuffer = overflow;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
log.warn({ err, sessionId: this.sessionId }, "[TeamsMessageDraft] flush: overflow send failed");
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
this.firstFlushPending = false;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const snapshot = this.buffer;
|
|
90
|
-
if (!this.ref?.activityId) {
|
|
91
|
-
// First message — send via context.send() to get the activityId
|
|
92
|
-
this.firstFlushPending = true;
|
|
93
|
-
try {
|
|
94
|
-
const result = await this.sendQueue.enqueue(() => sendText(this.context, snapshot), { type: "other" });
|
|
95
|
-
if (result) {
|
|
96
|
-
this.ref = {
|
|
97
|
-
activityId: result.id,
|
|
98
|
-
conversationId: this.context.activity.conversation?.id,
|
|
99
|
-
serviceUrl: this.context.activity.serviceUrl,
|
|
100
|
-
};
|
|
101
|
-
this.lastSentBuffer = snapshot;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
log.warn({ err, sessionId: this.sessionId }, "[TeamsMessageDraft] flush: initial send failed");
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
this.firstFlushPending = false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// Subsequent updates — use Bot Framework REST API to edit the message
|
|
113
|
-
if (snapshot === this.lastSentBuffer)
|
|
114
|
-
return;
|
|
115
|
-
try {
|
|
116
|
-
const success = await this.updateActivityViaRest(snapshot);
|
|
117
|
-
if (success) {
|
|
118
|
-
this.lastSentBuffer = snapshot;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (err) {
|
|
122
|
-
log.warn({ err, sessionId: this.sessionId }, "[TeamsMessageDraft] flush: update failed");
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Update an existing message via the Bot Framework REST API.
|
|
128
|
-
* Bypasses the teams.apps SDK context which doesn't support updateActivity.
|
|
129
|
-
*/
|
|
130
|
-
async updateActivityViaRest(text) {
|
|
131
|
-
if (!this.ref?.activityId || !this.ref.serviceUrl || !this.ref.conversationId)
|
|
132
|
-
return false;
|
|
133
|
-
if (!this.acquireBotToken)
|
|
134
|
-
return false;
|
|
135
|
-
const token = await this.acquireBotToken();
|
|
136
|
-
if (!token)
|
|
137
|
-
return false;
|
|
138
|
-
const url = `${this.ref.serviceUrl}/v3/conversations/${encodeURIComponent(this.ref.conversationId)}/activities/${encodeURIComponent(this.ref.activityId)}`;
|
|
139
|
-
const response = await fetch(url, {
|
|
140
|
-
method: "PUT",
|
|
141
|
-
headers: {
|
|
142
|
-
"Content-Type": "application/json",
|
|
143
|
-
"Authorization": `Bearer ${token}`,
|
|
144
|
-
},
|
|
145
|
-
body: JSON.stringify({
|
|
146
|
-
type: "message",
|
|
147
|
-
text: text.replace(/(?<!\n)\n(?!\n)/g, "\n\n"), // Teams newline normalization
|
|
148
|
-
textFormat: "markdown",
|
|
149
|
-
}),
|
|
150
|
-
});
|
|
151
|
-
if (!response.ok) {
|
|
152
|
-
log.warn({ status: response.status, sessionId: this.sessionId }, "[TeamsMessageDraft] REST updateActivity failed");
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
async stripPattern(pattern) {
|
|
158
|
-
if (!this.buffer)
|
|
159
|
-
return;
|
|
160
|
-
try {
|
|
161
|
-
this.buffer = this.buffer.replace(pattern, "").trim();
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
// Regex failed — leave buffer unchanged
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async finalize() {
|
|
168
|
-
if (this.finalizing)
|
|
169
|
-
return;
|
|
170
|
-
this.finalizing = true;
|
|
171
|
-
try {
|
|
172
|
-
await this._finalizeInner();
|
|
173
|
-
}
|
|
174
|
-
finally {
|
|
175
|
-
this.finalizing = false;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
async _finalizeInner() {
|
|
179
|
-
if (this.flushTimer) {
|
|
180
|
-
clearTimeout(this.flushTimer);
|
|
181
|
-
this.flushTimer = undefined;
|
|
182
|
-
}
|
|
183
|
-
// Wait for any in-flight flush to complete
|
|
184
|
-
await this.flushPromise;
|
|
185
|
-
if (!this.buffer)
|
|
186
|
-
return;
|
|
187
|
-
// If we have an activityId and the buffer fits in one message, do a final update
|
|
188
|
-
if (this.ref?.activityId && this.buffer.length <= MAX_DISPLAY_LENGTH) {
|
|
189
|
-
if (this.buffer !== this.lastSentBuffer) {
|
|
190
|
-
try {
|
|
191
|
-
const success = await this.updateActivityViaRest(this.buffer);
|
|
192
|
-
if (success)
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
// Fall through to send as new message
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
return; // Already sent, nothing to update
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// Buffer exceeds single message limit or update failed — send as new message(s).
|
|
204
|
-
// If we have an existing streaming message, update it with the first chunk,
|
|
205
|
-
// then send the rest as new messages.
|
|
206
|
-
const chunks = splitMessage(this.buffer, MAX_DISPLAY_LENGTH);
|
|
207
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
208
|
-
const content = chunks[i];
|
|
209
|
-
try {
|
|
210
|
-
if (i === 0 && this.ref?.activityId) {
|
|
211
|
-
// Update the existing streaming message with the first chunk
|
|
212
|
-
const success = await this.updateActivityViaRest(content);
|
|
213
|
-
if (!success) {
|
|
214
|
-
// Update failed — send as new message instead
|
|
215
|
-
await this.sendQueue.enqueue(() => sendText(this.context, content), { type: "other" });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
await this.sendQueue.enqueue(() => sendText(this.context, content), { type: "other" });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
log.warn({ err, sessionId: this.sessionId, chunk: i }, "[TeamsMessageDraft] finalize: chunk send failed");
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
export class TeamsDraftManager {
|
|
229
|
-
sendQueue;
|
|
230
|
-
acquireBotToken;
|
|
231
|
-
drafts = new Map();
|
|
232
|
-
constructor(sendQueue, acquireBotToken) {
|
|
233
|
-
this.sendQueue = sendQueue;
|
|
234
|
-
this.acquireBotToken = acquireBotToken;
|
|
235
|
-
}
|
|
236
|
-
getOrCreate(sessionId, context) {
|
|
237
|
-
let draft = this.drafts.get(sessionId);
|
|
238
|
-
if (!draft) {
|
|
239
|
-
draft = new TeamsMessageDraft(context, this.sendQueue, sessionId, this.acquireBotToken);
|
|
240
|
-
this.drafts.set(sessionId, draft);
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
// Re-bind to the latest TurnContext to prevent stale context references
|
|
244
|
-
// when a new inbound message arrives on the same session (new turn).
|
|
245
|
-
draft.updateContext(context);
|
|
246
|
-
}
|
|
247
|
-
return draft;
|
|
248
|
-
}
|
|
249
|
-
hasDraft(sessionId) {
|
|
250
|
-
return this.drafts.has(sessionId);
|
|
251
|
-
}
|
|
252
|
-
getDraft(sessionId) {
|
|
253
|
-
return this.drafts.get(sessionId);
|
|
254
|
-
}
|
|
255
|
-
async finalize(sessionId, _context, _isAssistant) {
|
|
256
|
-
const draft = this.drafts.get(sessionId);
|
|
257
|
-
if (!draft)
|
|
258
|
-
return;
|
|
259
|
-
// Delete BEFORE awaiting to prevent concurrent finalize() calls
|
|
260
|
-
// from double-finalizing the same draft (matches Telegram pattern)
|
|
261
|
-
this.drafts.delete(sessionId);
|
|
262
|
-
await draft.finalize();
|
|
263
|
-
}
|
|
264
|
-
cleanup(sessionId) {
|
|
265
|
-
this.drafts.delete(sessionId);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
//# sourceMappingURL=draft-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"draft-manager.js","sourceRoot":"","sources":["../src/draft-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/renderer.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { OutgoingMessage, NotificationMessage, DisplayVerbosity } from "@openacp/plugin-sdk";
|
|
2
|
-
import { BaseRenderer } from "@openacp/plugin-sdk";
|
|
3
|
-
export declare class TeamsRenderer extends BaseRenderer {
|
|
4
|
-
renderToolCall(content: OutgoingMessage, verbosity: DisplayVerbosity): {
|
|
5
|
-
body: string;
|
|
6
|
-
format: "markdown";
|
|
7
|
-
};
|
|
8
|
-
renderToolUpdate(content: OutgoingMessage, verbosity: DisplayVerbosity): {
|
|
9
|
-
body: string;
|
|
10
|
-
format: "markdown";
|
|
11
|
-
};
|
|
12
|
-
renderPlan(content: OutgoingMessage): {
|
|
13
|
-
body: string;
|
|
14
|
-
format: "markdown";
|
|
15
|
-
};
|
|
16
|
-
renderUsage(content: OutgoingMessage, verbosity: DisplayVerbosity): {
|
|
17
|
-
body: string;
|
|
18
|
-
format: "markdown";
|
|
19
|
-
};
|
|
20
|
-
renderError(content: OutgoingMessage): {
|
|
21
|
-
body: string;
|
|
22
|
-
format: "markdown";
|
|
23
|
-
};
|
|
24
|
-
renderNotification(notification: NotificationMessage): {
|
|
25
|
-
body: string;
|
|
26
|
-
format: "markdown";
|
|
27
|
-
};
|
|
28
|
-
renderSystemMessage(content: OutgoingMessage): {
|
|
29
|
-
body: string;
|
|
30
|
-
format: "markdown";
|
|
31
|
-
};
|
|
32
|
-
renderSessionEnd(_content: OutgoingMessage): {
|
|
33
|
-
body: string;
|
|
34
|
-
format: "markdown";
|
|
35
|
-
};
|
|
36
|
-
renderModeChange(content: OutgoingMessage): {
|
|
37
|
-
body: string;
|
|
38
|
-
format: "markdown";
|
|
39
|
-
};
|
|
40
|
-
renderConfigUpdate(content?: OutgoingMessage): {
|
|
41
|
-
body: string;
|
|
42
|
-
format: "markdown";
|
|
43
|
-
};
|
|
44
|
-
renderModelUpdate(content: OutgoingMessage): {
|
|
45
|
-
body: string;
|
|
46
|
-
format: "markdown";
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=renderer.d.ts.map
|
package/dist/renderer.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAA2C,MAAM,qBAAqB,CAAC;AAC3I,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAK3G,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAK7G,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAK1E,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAKxG,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAI3E,kBAAkB,CAAC,YAAY,EAAE,mBAAmB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAa3F,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAInF,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAIjF,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAKhF,kBAAkB,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;IAMnF,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE;CAIlF"}
|