@dbx-tools/teams 0.3.44 → 0.4.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/lib/index.d.ts +28 -0
- package/lib/index.js +24 -0
- package/lib/src/auth.d.ts +94 -0
- package/lib/src/auth.js +232 -0
- package/lib/src/builder.d.ts +29 -0
- package/lib/src/builder.js +96 -0
- package/lib/src/config.d.ts +148 -0
- package/lib/src/config.js +163 -0
- package/lib/src/connector.d.ts +53 -0
- package/lib/src/connector.js +82 -0
- package/lib/src/conversation.d.ts +234 -0
- package/lib/src/conversation.js +509 -0
- package/lib/src/defaults.d.ts +59 -0
- package/lib/src/defaults.js +61 -0
- package/lib/src/messaging.d.ts +74 -0
- package/lib/src/messaging.js +114 -0
- package/lib/src/plugin.d.ts +185 -0
- package/lib/src/plugin.js +430 -0
- package/lib/src/runtime.d.ts +63 -0
- package/lib/src/runtime.js +143 -0
- package/lib/src/tool.d.ts +67 -0
- package/lib/src/tool.js +71 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -7
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptor defaults for the Teams plugin.
|
|
3
|
+
*
|
|
4
|
+
* Building a card is a pure transform, but it still runs through
|
|
5
|
+
* `Plugin.execute()` so it shares the app's telemetry / timeout posture; the
|
|
6
|
+
* one operation that does I/O is posting a card to a Teams incoming webhook.
|
|
7
|
+
* The settings are kept here rather than at the call sites so the caching /
|
|
8
|
+
* retry / timeout posture of each is reviewable in one place.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The `PluginExecuteConfig` slice this package sets. Mirrored structurally
|
|
14
|
+
* because AppKit's `PluginExecuteConfig` lives behind a subpath its `exports`
|
|
15
|
+
* map does not publish, so the nominal type cannot be imported. Written as a
|
|
16
|
+
* type alias rather than an interface so it stays assignable to the nominal
|
|
17
|
+
* type's index signature.
|
|
18
|
+
*/
|
|
19
|
+
export type TeamsExecuteConfig = {
|
|
20
|
+
cache?: {
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
ttl?: number;
|
|
23
|
+
cacheKey?: (string | number | object)[];
|
|
24
|
+
};
|
|
25
|
+
retry?: {
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
attempts?: number;
|
|
28
|
+
initialDelay?: number;
|
|
29
|
+
maxDelay?: number;
|
|
30
|
+
};
|
|
31
|
+
timeout?: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The `PluginExecutionSettings` shape accepted by AppKit's `Plugin.execute()`.
|
|
35
|
+
* Mirrored structurally for the same reason as {@link TeamsExecuteConfig}.
|
|
36
|
+
*/
|
|
37
|
+
export type TeamsExecutionSettings = {
|
|
38
|
+
default: TeamsExecuteConfig;
|
|
39
|
+
user?: TeamsExecuteConfig;
|
|
40
|
+
};
|
|
41
|
+
/** Ceiling on how long a single card build may take. */
|
|
42
|
+
export declare const BUILD_TIMEOUT_MS = 5000;
|
|
43
|
+
/** Ceiling on how long a single webhook POST may take. */
|
|
44
|
+
export declare const POST_TIMEOUT_MS = 15000;
|
|
45
|
+
/** Attempts allowed for a webhook POST, including the first. */
|
|
46
|
+
export declare const POST_ATTEMPTS = 3;
|
|
47
|
+
/** Execution settings for building a card (a pure, in-process transform). */
|
|
48
|
+
export declare const TEAMS_BUILD_SETTINGS: TeamsExecutionSettings;
|
|
49
|
+
/**
|
|
50
|
+
* Ceiling on one conversation turn. Generous relative to the other two: a turn
|
|
51
|
+
* spans a full agent call (model latency plus any tool the agent runs), so it is
|
|
52
|
+
* bounded by the same order of magnitude as a chat request rather than a
|
|
53
|
+
* transform.
|
|
54
|
+
*/
|
|
55
|
+
export declare const TURN_TIMEOUT_MS = 120000;
|
|
56
|
+
/** Execution settings for one Teams conversation turn (an agent call). */
|
|
57
|
+
export declare const TEAMS_TURN_SETTINGS: TeamsExecutionSettings;
|
|
58
|
+
/** Execution settings for posting a card to a Teams incoming webhook. */
|
|
59
|
+
export declare const TEAMS_POST_SETTINGS: TeamsExecutionSettings;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptor defaults for the Teams plugin.
|
|
3
|
+
*
|
|
4
|
+
* Building a card is a pure transform, but it still runs through
|
|
5
|
+
* `Plugin.execute()` so it shares the app's telemetry / timeout posture; the
|
|
6
|
+
* one operation that does I/O is posting a card to a Teams incoming webhook.
|
|
7
|
+
* The settings are kept here rather than at the call sites so the caching /
|
|
8
|
+
* retry / timeout posture of each is reviewable in one place.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/** Ceiling on how long a single card build may take. */
|
|
13
|
+
export const BUILD_TIMEOUT_MS = 5_000;
|
|
14
|
+
/** Ceiling on how long a single webhook POST may take. */
|
|
15
|
+
export const POST_TIMEOUT_MS = 15_000;
|
|
16
|
+
/** Attempts allowed for a webhook POST, including the first. */
|
|
17
|
+
export const POST_ATTEMPTS = 3;
|
|
18
|
+
/** Execution settings for building a card (a pure, in-process transform). */
|
|
19
|
+
export const TEAMS_BUILD_SETTINGS = {
|
|
20
|
+
default: {
|
|
21
|
+
// Cache disabled: the build is cheap and deterministic, so a cache would
|
|
22
|
+
// add a cross-identity key surface for no measurable saving.
|
|
23
|
+
cache: { enabled: false },
|
|
24
|
+
// Retry disabled: the transform performs no I/O, so a failure is
|
|
25
|
+
// deterministic and a second attempt would fail identically.
|
|
26
|
+
retry: { enabled: false },
|
|
27
|
+
timeout: BUILD_TIMEOUT_MS,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Ceiling on one conversation turn. Generous relative to the other two: a turn
|
|
32
|
+
* spans a full agent call (model latency plus any tool the agent runs), so it is
|
|
33
|
+
* bounded by the same order of magnitude as a chat request rather than a
|
|
34
|
+
* transform.
|
|
35
|
+
*/
|
|
36
|
+
export const TURN_TIMEOUT_MS = 120_000;
|
|
37
|
+
/** Execution settings for one Teams conversation turn (an agent call). */
|
|
38
|
+
export const TEAMS_TURN_SETTINGS = {
|
|
39
|
+
default: {
|
|
40
|
+
// Cache disabled: a turn is conversational and stateful - the same text in
|
|
41
|
+
// a different conversation must not replay an earlier card.
|
|
42
|
+
cache: { enabled: false },
|
|
43
|
+
// Retry disabled: a turn may have run tools with side effects before it
|
|
44
|
+
// failed, and re-running the model would double them.
|
|
45
|
+
retry: { enabled: false },
|
|
46
|
+
timeout: TURN_TIMEOUT_MS,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
/** Execution settings for posting a card to a Teams incoming webhook. */
|
|
50
|
+
export const TEAMS_POST_SETTINGS = {
|
|
51
|
+
default: {
|
|
52
|
+
// Cache disabled: a post is a side effect, not a value. Replaying a cached
|
|
53
|
+
// result would report a delivery that never happened.
|
|
54
|
+
cache: { enabled: false },
|
|
55
|
+
// Retry enabled: a webhook POST is idempotent enough that a transient 5xx
|
|
56
|
+
// or timeout at the Teams edge is worth one or two more attempts.
|
|
57
|
+
retry: { enabled: true, attempts: POST_ATTEMPTS },
|
|
58
|
+
timeout: POST_TIMEOUT_MS,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVmYXVsdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGVmYXVsdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQXdCSCx3REFBd0Q7QUFDeEQsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxDQUFDO0FBRXRDLDBEQUEwRDtBQUMxRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDO0FBRXRDLGdFQUFnRTtBQUNoRSxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0FBRS9CLDZFQUE2RTtBQUM3RSxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBMkI7SUFDMUQsT0FBTyxFQUFFO1FBQ1AseUVBQXlFO1FBQ3pFLDZEQUE2RDtRQUM3RCxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1FBQ3pCLGlFQUFpRTtRQUNqRSw2REFBNkQ7UUFDN0QsS0FBSyxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRTtRQUN6QixPQUFPLEVBQUUsZ0JBQWdCO0tBQzFCO0NBQ0YsQ0FBQztBQUVGOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUV2QywwRUFBMEU7QUFDMUUsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQTJCO0lBQ3pELE9BQU8sRUFBRTtRQUNQLDJFQUEyRTtRQUMzRSw0REFBNEQ7UUFDNUQsS0FBSyxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRTtRQUN6Qix3RUFBd0U7UUFDeEUsc0RBQXNEO1FBQ3RELEtBQUssRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUU7UUFDekIsT0FBTyxFQUFFLGVBQWU7S0FDekI7Q0FDRixDQUFDO0FBRUYseUVBQXlFO0FBQ3pFLE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUEyQjtJQUN6RCxPQUFPLEVBQUU7UUFDUCwyRUFBMkU7UUFDM0Usc0RBQXNEO1FBQ3RELEtBQUssRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUU7UUFDekIsMEVBQTBFO1FBQzFFLGtFQUFrRTtRQUNsRSxLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQUU7UUFDakQsT0FBTyxFQUFFLGVBQWU7S0FDekI7Q0FDRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJbnRlcmNlcHRvciBkZWZhdWx0cyBmb3IgdGhlIFRlYW1zIHBsdWdpbi5cbiAqXG4gKiBCdWlsZGluZyBhIGNhcmQgaXMgYSBwdXJlIHRyYW5zZm9ybSwgYnV0IGl0IHN0aWxsIHJ1bnMgdGhyb3VnaFxuICogYFBsdWdpbi5leGVjdXRlKClgIHNvIGl0IHNoYXJlcyB0aGUgYXBwJ3MgdGVsZW1ldHJ5IC8gdGltZW91dCBwb3N0dXJlOyB0aGVcbiAqIG9uZSBvcGVyYXRpb24gdGhhdCBkb2VzIEkvTyBpcyBwb3N0aW5nIGEgY2FyZCB0byBhIFRlYW1zIGluY29taW5nIHdlYmhvb2suXG4gKiBUaGUgc2V0dGluZ3MgYXJlIGtlcHQgaGVyZSByYXRoZXIgdGhhbiBhdCB0aGUgY2FsbCBzaXRlcyBzbyB0aGUgY2FjaGluZyAvXG4gKiByZXRyeSAvIHRpbWVvdXQgcG9zdHVyZSBvZiBlYWNoIGlzIHJldmlld2FibGUgaW4gb25lIHBsYWNlLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG4vKipcbiAqIFRoZSBgUGx1Z2luRXhlY3V0ZUNvbmZpZ2Agc2xpY2UgdGhpcyBwYWNrYWdlIHNldHMuIE1pcnJvcmVkIHN0cnVjdHVyYWxseVxuICogYmVjYXVzZSBBcHBLaXQncyBgUGx1Z2luRXhlY3V0ZUNvbmZpZ2AgbGl2ZXMgYmVoaW5kIGEgc3VicGF0aCBpdHMgYGV4cG9ydHNgXG4gKiBtYXAgZG9lcyBub3QgcHVibGlzaCwgc28gdGhlIG5vbWluYWwgdHlwZSBjYW5ub3QgYmUgaW1wb3J0ZWQuIFdyaXR0ZW4gYXMgYVxuICogdHlwZSBhbGlhcyByYXRoZXIgdGhhbiBhbiBpbnRlcmZhY2Ugc28gaXQgc3RheXMgYXNzaWduYWJsZSB0byB0aGUgbm9taW5hbFxuICogdHlwZSdzIGluZGV4IHNpZ25hdHVyZS5cbiAqL1xuZXhwb3J0IHR5cGUgVGVhbXNFeGVjdXRlQ29uZmlnID0ge1xuICBjYWNoZT86IHsgZW5hYmxlZD86IGJvb2xlYW47IHR0bD86IG51bWJlcjsgY2FjaGVLZXk/OiAoc3RyaW5nIHwgbnVtYmVyIHwgb2JqZWN0KVtdIH07XG4gIHJldHJ5PzogeyBlbmFibGVkPzogYm9vbGVhbjsgYXR0ZW1wdHM/OiBudW1iZXI7IGluaXRpYWxEZWxheT86IG51bWJlcjsgbWF4RGVsYXk/OiBudW1iZXIgfTtcbiAgdGltZW91dD86IG51bWJlcjtcbn07XG5cbi8qKlxuICogVGhlIGBQbHVnaW5FeGVjdXRpb25TZXR0aW5nc2Agc2hhcGUgYWNjZXB0ZWQgYnkgQXBwS2l0J3MgYFBsdWdpbi5leGVjdXRlKClgLlxuICogTWlycm9yZWQgc3RydWN0dXJhbGx5IGZvciB0aGUgc2FtZSByZWFzb24gYXMge0BsaW5rIFRlYW1zRXhlY3V0ZUNvbmZpZ30uXG4gKi9cbmV4cG9ydCB0eXBlIFRlYW1zRXhlY3V0aW9uU2V0dGluZ3MgPSB7XG4gIGRlZmF1bHQ6IFRlYW1zRXhlY3V0ZUNvbmZpZztcbiAgdXNlcj86IFRlYW1zRXhlY3V0ZUNvbmZpZztcbn07XG5cbi8qKiBDZWlsaW5nIG9uIGhvdyBsb25nIGEgc2luZ2xlIGNhcmQgYnVpbGQgbWF5IHRha2UuICovXG5leHBvcnQgY29uc3QgQlVJTERfVElNRU9VVF9NUyA9IDVfMDAwO1xuXG4vKiogQ2VpbGluZyBvbiBob3cgbG9uZyBhIHNpbmdsZSB3ZWJob29rIFBPU1QgbWF5IHRha2UuICovXG5leHBvcnQgY29uc3QgUE9TVF9USU1FT1VUX01TID0gMTVfMDAwO1xuXG4vKiogQXR0ZW1wdHMgYWxsb3dlZCBmb3IgYSB3ZWJob29rIFBPU1QsIGluY2x1ZGluZyB0aGUgZmlyc3QuICovXG5leHBvcnQgY29uc3QgUE9TVF9BVFRFTVBUUyA9IDM7XG5cbi8qKiBFeGVjdXRpb24gc2V0dGluZ3MgZm9yIGJ1aWxkaW5nIGEgY2FyZCAoYSBwdXJlLCBpbi1wcm9jZXNzIHRyYW5zZm9ybSkuICovXG5leHBvcnQgY29uc3QgVEVBTVNfQlVJTERfU0VUVElOR1M6IFRlYW1zRXhlY3V0aW9uU2V0dGluZ3MgPSB7XG4gIGRlZmF1bHQ6IHtcbiAgICAvLyBDYWNoZSBkaXNhYmxlZDogdGhlIGJ1aWxkIGlzIGNoZWFwIGFuZCBkZXRlcm1pbmlzdGljLCBzbyBhIGNhY2hlIHdvdWxkXG4gICAgLy8gYWRkIGEgY3Jvc3MtaWRlbnRpdHkga2V5IHN1cmZhY2UgZm9yIG5vIG1lYXN1cmFibGUgc2F2aW5nLlxuICAgIGNhY2hlOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgLy8gUmV0cnkgZGlzYWJsZWQ6IHRoZSB0cmFuc2Zvcm0gcGVyZm9ybXMgbm8gSS9PLCBzbyBhIGZhaWx1cmUgaXNcbiAgICAvLyBkZXRlcm1pbmlzdGljIGFuZCBhIHNlY29uZCBhdHRlbXB0IHdvdWxkIGZhaWwgaWRlbnRpY2FsbHkuXG4gICAgcmV0cnk6IHsgZW5hYmxlZDogZmFsc2UgfSxcbiAgICB0aW1lb3V0OiBCVUlMRF9USU1FT1VUX01TLFxuICB9LFxufTtcblxuLyoqXG4gKiBDZWlsaW5nIG9uIG9uZSBjb252ZXJzYXRpb24gdHVybi4gR2VuZXJvdXMgcmVsYXRpdmUgdG8gdGhlIG90aGVyIHR3bzogYSB0dXJuXG4gKiBzcGFucyBhIGZ1bGwgYWdlbnQgY2FsbCAobW9kZWwgbGF0ZW5jeSBwbHVzIGFueSB0b29sIHRoZSBhZ2VudCBydW5zKSwgc28gaXQgaXNcbiAqIGJvdW5kZWQgYnkgdGhlIHNhbWUgb3JkZXIgb2YgbWFnbml0dWRlIGFzIGEgY2hhdCByZXF1ZXN0IHJhdGhlciB0aGFuIGFcbiAqIHRyYW5zZm9ybS5cbiAqL1xuZXhwb3J0IGNvbnN0IFRVUk5fVElNRU9VVF9NUyA9IDEyMF8wMDA7XG5cbi8qKiBFeGVjdXRpb24gc2V0dGluZ3MgZm9yIG9uZSBUZWFtcyBjb252ZXJzYXRpb24gdHVybiAoYW4gYWdlbnQgY2FsbCkuICovXG5leHBvcnQgY29uc3QgVEVBTVNfVFVSTl9TRVRUSU5HUzogVGVhbXNFeGVjdXRpb25TZXR0aW5ncyA9IHtcbiAgZGVmYXVsdDoge1xuICAgIC8vIENhY2hlIGRpc2FibGVkOiBhIHR1cm4gaXMgY29udmVyc2F0aW9uYWwgYW5kIHN0YXRlZnVsIC0gdGhlIHNhbWUgdGV4dCBpblxuICAgIC8vIGEgZGlmZmVyZW50IGNvbnZlcnNhdGlvbiBtdXN0IG5vdCByZXBsYXkgYW4gZWFybGllciBjYXJkLlxuICAgIGNhY2hlOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgLy8gUmV0cnkgZGlzYWJsZWQ6IGEgdHVybiBtYXkgaGF2ZSBydW4gdG9vbHMgd2l0aCBzaWRlIGVmZmVjdHMgYmVmb3JlIGl0XG4gICAgLy8gZmFpbGVkLCBhbmQgcmUtcnVubmluZyB0aGUgbW9kZWwgd291bGQgZG91YmxlIHRoZW0uXG4gICAgcmV0cnk6IHsgZW5hYmxlZDogZmFsc2UgfSxcbiAgICB0aW1lb3V0OiBUVVJOX1RJTUVPVVRfTVMsXG4gIH0sXG59O1xuXG4vKiogRXhlY3V0aW9uIHNldHRpbmdzIGZvciBwb3N0aW5nIGEgY2FyZCB0byBhIFRlYW1zIGluY29taW5nIHdlYmhvb2suICovXG5leHBvcnQgY29uc3QgVEVBTVNfUE9TVF9TRVRUSU5HUzogVGVhbXNFeGVjdXRpb25TZXR0aW5ncyA9IHtcbiAgZGVmYXVsdDoge1xuICAgIC8vIENhY2hlIGRpc2FibGVkOiBhIHBvc3QgaXMgYSBzaWRlIGVmZmVjdCwgbm90IGEgdmFsdWUuIFJlcGxheWluZyBhIGNhY2hlZFxuICAgIC8vIHJlc3VsdCB3b3VsZCByZXBvcnQgYSBkZWxpdmVyeSB0aGF0IG5ldmVyIGhhcHBlbmVkLlxuICAgIGNhY2hlOiB7IGVuYWJsZWQ6IGZhbHNlIH0sXG4gICAgLy8gUmV0cnkgZW5hYmxlZDogYSB3ZWJob29rIFBPU1QgaXMgaWRlbXBvdGVudCBlbm91Z2ggdGhhdCBhIHRyYW5zaWVudCA1eHhcbiAgICAvLyBvciB0aW1lb3V0IGF0IHRoZSBUZWFtcyBlZGdlIGlzIHdvcnRoIG9uZSBvciB0d28gbW9yZSBhdHRlbXB0cy5cbiAgICByZXRyeTogeyBlbmFibGVkOiB0cnVlLCBhdHRlbXB0czogUE9TVF9BVFRFTVBUUyB9LFxuICAgIHRpbWVvdXQ6IFBPU1RfVElNRU9VVF9NUyxcbiAgfSxcbn07XG4iXX0=
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Teams messaging endpoint's turn: what happens between Azure Bot Service
|
|
3
|
+
* POSTing an activity and a card appearing in the channel.
|
|
4
|
+
*
|
|
5
|
+
* Split out from `conversation.ts` because the two callers have genuinely
|
|
6
|
+
* different contracts. `conversation.ts` runs a turn and RETURNS the reply, which
|
|
7
|
+
* is what a direct HTTP caller (or the in-repo preview UI) wants. A real channel
|
|
8
|
+
* cannot work that way: Bot Service ignores the response body and expects a fast
|
|
9
|
+
* `200`, so this module acknowledges first and delivers the reply out-of-band
|
|
10
|
+
* through the Connector API.
|
|
11
|
+
*
|
|
12
|
+
* The sequence for a `message` activity:
|
|
13
|
+
*
|
|
14
|
+
* 1. validate the inbound JWT and pin the reply destination to the
|
|
15
|
+
* token's `serviceUrl` (see `auth.ts`);
|
|
16
|
+
* 2. return `200` immediately - Bot Service retries an activity it thinks
|
|
17
|
+
* timed out, and a duplicate turn means a duplicate card;
|
|
18
|
+
* 3. show the typing indicator, run the agent, and POST the card back.
|
|
19
|
+
*
|
|
20
|
+
* Step 2 is why this is fire-and-forget rather than awaited: a card-producing
|
|
21
|
+
* agent turn takes seconds to tens of seconds, far longer than the ~15s Bot
|
|
22
|
+
* Service allows a bot to acknowledge.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { activity as activityContract } from "@dbx-tools/shared-teams";
|
|
27
|
+
import { type CardAgentLike, type CardContextFactory } from "./conversation.js";
|
|
28
|
+
/** Resolved bot credentials a delivered turn needs. */
|
|
29
|
+
export interface BotCredentials {
|
|
30
|
+
/** Entra app (client) id of the bot registration. */
|
|
31
|
+
appId: string;
|
|
32
|
+
/** Client secret for {@link appId}. */
|
|
33
|
+
appPassword: string;
|
|
34
|
+
/** Tenant id for a single-tenant bot. */
|
|
35
|
+
appTenantId?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Everything {@link deliverTurn} needs to answer one inbound activity. */
|
|
38
|
+
export interface DeliverTurnOptions {
|
|
39
|
+
/** The agent that composes the card. */
|
|
40
|
+
agent: CardAgentLike;
|
|
41
|
+
/** The validated inbound activity. */
|
|
42
|
+
activity: activityContract.Activity;
|
|
43
|
+
/** Bot credentials used to fetch the outbound Connector token. */
|
|
44
|
+
credentials: BotCredentials;
|
|
45
|
+
/**
|
|
46
|
+
* Reply destination, already validated against the inbound token. Passing this
|
|
47
|
+
* explicitly (rather than reading `activity.serviceUrl`) keeps the security
|
|
48
|
+
* decision in the caller, where the token is in scope.
|
|
49
|
+
*/
|
|
50
|
+
serviceUrl: string;
|
|
51
|
+
/**
|
|
52
|
+
* Builds the agent's per-turn request context, so the delivered turn has the
|
|
53
|
+
* same tool reach (Genie and every other user-scoped tool) as a chat turn.
|
|
54
|
+
*/
|
|
55
|
+
createRequestContext?: CardContextFactory;
|
|
56
|
+
/** Cancels the turn (process shutdown, or a test tearing down). */
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Read the reply destination off an inbound activity, if it names a usable one.
|
|
61
|
+
*
|
|
62
|
+
* `serviceUrl` rides on the activity as a plain string, so it is validated
|
|
63
|
+
* against the verified token's own `serviceurl` claim before anything
|
|
64
|
+
* authenticated is sent there.
|
|
65
|
+
*/
|
|
66
|
+
export declare const resolveServiceUrl: (activity: activityContract.Activity, tokenServiceUrl?: string) => string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Run one turn and deliver the card back through the Connector API.
|
|
69
|
+
*
|
|
70
|
+
* Awaited by nobody on the request path (the route has already answered `200`),
|
|
71
|
+
* so this owns its own error handling: a failure posts {@link FAILURE_TEXT} to
|
|
72
|
+
* the channel and is logged, never rethrown into an unhandled rejection.
|
|
73
|
+
*/
|
|
74
|
+
export declare const deliverTurn: (options: DeliverTurnOptions) => Promise<void>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Teams messaging endpoint's turn: what happens between Azure Bot Service
|
|
3
|
+
* POSTing an activity and a card appearing in the channel.
|
|
4
|
+
*
|
|
5
|
+
* Split out from `conversation.ts` because the two callers have genuinely
|
|
6
|
+
* different contracts. `conversation.ts` runs a turn and RETURNS the reply, which
|
|
7
|
+
* is what a direct HTTP caller (or the in-repo preview UI) wants. A real channel
|
|
8
|
+
* cannot work that way: Bot Service ignores the response body and expects a fast
|
|
9
|
+
* `200`, so this module acknowledges first and delivers the reply out-of-band
|
|
10
|
+
* through the Connector API.
|
|
11
|
+
*
|
|
12
|
+
* The sequence for a `message` activity:
|
|
13
|
+
*
|
|
14
|
+
* 1. validate the inbound JWT and pin the reply destination to the
|
|
15
|
+
* token's `serviceUrl` (see `auth.ts`);
|
|
16
|
+
* 2. return `200` immediately - Bot Service retries an activity it thinks
|
|
17
|
+
* timed out, and a duplicate turn means a duplicate card;
|
|
18
|
+
* 3. show the typing indicator, run the agent, and POST the card back.
|
|
19
|
+
*
|
|
20
|
+
* Step 2 is why this is fire-and-forget rather than awaited: a card-producing
|
|
21
|
+
* agent turn takes seconds to tens of seconds, far longer than the ~15s Bot
|
|
22
|
+
* Service allows a bot to acknowledge.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { error, log } from "@dbx-tools/shared-core";
|
|
27
|
+
import { connectorToken, isAllowedServiceUrl } from "./auth.js";
|
|
28
|
+
import { sendActivity, sendTyping } from "./connector.js";
|
|
29
|
+
import { runCardTurn } from "./conversation.js";
|
|
30
|
+
const logger = log.logger("teams:messaging");
|
|
31
|
+
/**
|
|
32
|
+
* Message shown in the channel when a turn fails.
|
|
33
|
+
*
|
|
34
|
+
* A bot that silently drops a failed turn looks broken - the user sees their
|
|
35
|
+
* message land and nothing come back, forever. A short apology is posted instead
|
|
36
|
+
* so the conversation stays legible; the real error goes to the logs.
|
|
37
|
+
*/
|
|
38
|
+
const FAILURE_TEXT = "Sorry - I could not put together an answer for that. Please try again.";
|
|
39
|
+
/**
|
|
40
|
+
* Read the reply destination off an inbound activity, if it names a usable one.
|
|
41
|
+
*
|
|
42
|
+
* `serviceUrl` rides on the activity as a plain string, so it is validated
|
|
43
|
+
* against the verified token's own `serviceurl` claim before anything
|
|
44
|
+
* authenticated is sent there.
|
|
45
|
+
*/
|
|
46
|
+
export const resolveServiceUrl = (activity, tokenServiceUrl) => {
|
|
47
|
+
const raw = activity.serviceUrl;
|
|
48
|
+
const candidate = typeof raw === "string" ? raw.trim() : "";
|
|
49
|
+
if (!candidate)
|
|
50
|
+
return null;
|
|
51
|
+
return isAllowedServiceUrl(candidate, tokenServiceUrl) ? candidate : null;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Run one turn and deliver the card back through the Connector API.
|
|
55
|
+
*
|
|
56
|
+
* Awaited by nobody on the request path (the route has already answered `200`),
|
|
57
|
+
* so this owns its own error handling: a failure posts {@link FAILURE_TEXT} to
|
|
58
|
+
* the channel and is logged, never rethrown into an unhandled rejection.
|
|
59
|
+
*/
|
|
60
|
+
export const deliverTurn = async (options) => {
|
|
61
|
+
const { agent, activity, credentials, serviceUrl } = options;
|
|
62
|
+
const conversationId = activity.conversation?.id;
|
|
63
|
+
if (!conversationId) {
|
|
64
|
+
logger.warn("dropping activity with no conversation id");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const base = {
|
|
68
|
+
serviceUrl,
|
|
69
|
+
conversationId,
|
|
70
|
+
...(activity.id ? { replyToId: activity.id } : {}),
|
|
71
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
72
|
+
};
|
|
73
|
+
let token;
|
|
74
|
+
try {
|
|
75
|
+
token = await connectorToken({
|
|
76
|
+
appId: credentials.appId,
|
|
77
|
+
appPassword: credentials.appPassword,
|
|
78
|
+
...(credentials.appTenantId ? { appTenantId: credentials.appTenantId } : {}),
|
|
79
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
// No token means nothing can be delivered - not even the apology - so this
|
|
84
|
+
// is the one failure that can only be logged.
|
|
85
|
+
logger.error("could not obtain a connector token", { error: error.errorMessage(err) });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const target = { ...base, token };
|
|
89
|
+
await sendTyping(target);
|
|
90
|
+
try {
|
|
91
|
+
const activities = await runCardTurn(agent, activity, {
|
|
92
|
+
...(options.createRequestContext
|
|
93
|
+
? { createRequestContext: options.createRequestContext }
|
|
94
|
+
: {}),
|
|
95
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
96
|
+
});
|
|
97
|
+
for (const reply of activities) {
|
|
98
|
+
await sendActivity(reply, target);
|
|
99
|
+
}
|
|
100
|
+
logger.info("turn delivered", { conversation: conversationId, replies: activities.length });
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger.error("turn failed", { conversation: conversationId, error: error.errorMessage(err) });
|
|
104
|
+
try {
|
|
105
|
+
await sendActivity({ type: "message", text: FAILURE_TEXT }, target);
|
|
106
|
+
}
|
|
107
|
+
catch (postErr) {
|
|
108
|
+
logger.error("could not report the failure to the channel", {
|
|
109
|
+
error: error.errorMessage(postErr),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21lc3NhZ2luZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHO0FBRUgsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQzdELE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3ZELE9BQU8sRUFBRSxXQUFXLEVBQStDLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBRTdDOzs7Ozs7R0FNRztBQUNILE1BQU0sWUFBWSxHQUFHLHdFQUF3RSxDQUFDO0FBbUM5Rjs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxDQUMvQixRQUFtQyxFQUNuQyxlQUF3QixFQUNULEVBQUU7SUFDakIsTUFBTSxHQUFHLEdBQUksUUFBcUMsQ0FBQyxVQUFVLENBQUM7SUFDOUQsTUFBTSxTQUFTLEdBQUcsT0FBTyxHQUFHLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUM1RCxJQUFJLENBQUMsU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzVCLE9BQU8sbUJBQW1CLENBQUMsU0FBUyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztBQUM1RSxDQUFDLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsS0FBSyxFQUFFLE9BQTJCLEVBQWlCLEVBQUU7SUFDOUUs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QUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3RFLENBQUM7UUFBQyxPQUFPLE9BQU8sRUFBRSxDQUFDO1lBQ2pCLE1BQU0sQ0FBQyxLQUFLLENBQUMsNkNBQTZDLEVBQUU7Z0JBQzFELEtBQUssRUFBRSxLQUFLLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQzthQUNuQyxDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhlIFRlYW1zIG1lc3NhZ2luZyBlbmRwb2ludCdzIHR1cm46IHdoYXQgaGFwcGVucyBiZXR3ZWVuIEF6dXJlIEJvdCBTZXJ2aWNlXG4gKiBQT1NUaW5nIGFuIGFjdGl2aXR5IGFuZCBhIGNhcmQgYXBwZWFyaW5nIGluIHRoZSBjaGFubmVsLlxuICpcbiAqIFNwbGl0IG91dCBmcm9tIGBjb252ZXJzYXRpb24udHNgIGJlY2F1c2UgdGhlIHR3byBjYWxsZXJzIGhhdmUgZ2VudWluZWx5XG4gKiBkaWZmZXJlbnQgY29udHJhY3RzLiBgY29udmVyc2F0aW9uLnRzYCBydW5zIGEgdHVybiBhbmQgUkVUVVJOUyB0aGUgcmVwbHksIHdoaWNoXG4gKiBpcyB3aGF0IGEgZGlyZWN0IEhUVFAgY2FsbGVyIChvciB0aGUgaW4tcmVwbyBwcmV2aWV3IFVJKSB3YW50cy4gQSByZWFsIGNoYW5uZWxcbiAqIGNhbm5vdCB3b3JrIHRoYXQgd2F5OiBCb3QgU2VydmljZSBpZ25vcmVzIHRoZSByZXNwb25zZSBib2R5IGFuZCBleHBlY3RzIGEgZmFzdFxuICogYDIwMGAsIHNvIHRoaXMgbW9kdWxlIGFja25vd2xlZGdlcyBmaXJzdCBhbmQgZGVsaXZlcnMgdGhlIHJlcGx5IG91dC1vZi1iYW5kXG4gKiB0aHJvdWdoIHRoZSBDb25uZWN0b3IgQVBJLlxuICpcbiAqIFRoZSBzZXF1ZW5jZSBmb3IgYSBgbWVzc2FnZWAgYWN0aXZpdHk6XG4gKlxuICogICAxLiB2YWxpZGF0ZSB0aGUgaW5ib3VuZCBKV1QgYW5kIHBpbiB0aGUgcmVwbHkgZGVzdGluYXRpb24gdG8gdGhlXG4gKiAgICAgIHRva2VuJ3MgYHNlcnZpY2VVcmxgIChzZWUgYGF1dGgudHNgKTtcbiAqICAgMi4gcmV0dXJuIGAyMDBgIGltbWVkaWF0ZWx5IC0gQm90IFNlcnZpY2UgcmV0cmllcyBhbiBhY3Rpdml0eSBpdCB0aGlua3NcbiAqICAgICAgdGltZWQgb3V0LCBhbmQgYSBkdXBsaWNhdGUgdHVybiBtZWFucyBhIGR1cGxpY2F0ZSBjYXJkO1xuICogICAzLiBzaG93IHRoZSB0eXBpbmcgaW5kaWNhdG9yLCBydW4gdGhlIGFnZW50LCBhbmQgUE9TVCB0aGUgY2FyZCBiYWNrLlxuICpcbiAqIFN0ZXAgMiBpcyB3aHkgdGhpcyBpcyBmaXJlLWFuZC1mb3JnZXQgcmF0aGVyIHRoYW4gYXdhaXRlZDogYSBjYXJkLXByb2R1Y2luZ1xuICogYWdlbnQgdHVybiB0YWtlcyBzZWNvbmRzIHRvIHRlbnMgb2Ygc2Vjb25kcywgZmFyIGxvbmdlciB0aGFuIHRoZSB+MTVzIEJvdFxuICogU2VydmljZSBhbGxvd3MgYSBib3QgdG8gYWNrbm93bGVkZ2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGVycm9yLCBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgYWN0aXZpdHkgYXMgYWN0aXZpdHlDb250cmFjdCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC10ZWFtc1wiO1xuaW1wb3J0IHsgY29ubmVjdG9yVG9rZW4sIGlzQWxsb3dlZFNlcnZpY2VVcmwgfSBmcm9tIFwiLi9hdXRoXCI7XG5pbXBvcnQgeyBzZW5kQWN0aXZpdHksIHNlbmRUeXBpbmcgfSBmcm9tIFwiLi9jb25uZWN0b3JcIjtcbmltcG9ydCB7IHJ1bkNhcmRUdXJuLCB0eXBlIENhcmRBZ2VudExpa2UsIHR5cGUgQ2FyZENvbnRleHRGYWN0b3J5IH0gZnJvbSBcIi4vY29udmVyc2F0aW9uXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJ0ZWFtczptZXNzYWdpbmdcIik7XG5cbi8qKlxuICogTWVzc2FnZSBzaG93biBpbiB0aGUgY2hhbm5lbCB3aGVuIGEgdHVybiBmYWlscy5cbiAqXG4gKiBBIGJvdCB0aGF0IHNpbGVudGx5IGRyb3BzIGEgZmFpbGVkIHR1cm4gbG9va3MgYnJva2VuIC0gdGhlIHVzZXIgc2VlcyB0aGVpclxuICogbWVzc2FnZSBsYW5kIGFuZCBub3RoaW5nIGNvbWUgYmFjaywgZm9yZXZlci4gQSBzaG9ydCBhcG9sb2d5IGlzIHBvc3RlZCBpbnN0ZWFkXG4gKiBzbyB0aGUgY29udmVyc2F0aW9uIHN0YXlzIGxlZ2libGU7IHRoZSByZWFsIGVycm9yIGdvZXMgdG8gdGhlIGxvZ3MuXG4gKi9cbmNvbnN0IEZBSUxVUkVfVEVYVCA9IFwiU29ycnkgLSBJIGNvdWxkIG5vdCBwdXQgdG9nZXRoZXIgYW4gYW5zd2VyIGZvciB0aGF0LiBQbGVhc2UgdHJ5IGFnYWluLlwiO1xuXG4vKiogUmVzb2x2ZWQgYm90IGNyZWRlbnRpYWxzIGEgZGVsaXZlcmVkIHR1cm4gbmVlZHMuICovXG5leHBvcnQgaW50ZXJmYWNlIEJvdENyZWRlbnRpYWxzIHtcbiAgLyoqIEVudHJhIGFwcCAoY2xpZW50KSBpZCBvZiB0aGUgYm90IHJlZ2lzdHJhdGlvbi4gKi9cbiAgYXBwSWQ6IHN0cmluZztcbiAgLyoqIENsaWVudCBzZWNyZXQgZm9yIHtAbGluayBhcHBJZH0uICovXG4gIGFwcFBhc3N3b3JkOiBzdHJpbmc7XG4gIC8qKiBUZW5hbnQgaWQgZm9yIGEgc2luZ2xlLXRlbmFudCBib3QuICovXG4gIGFwcFRlbmFudElkPzogc3RyaW5nO1xufVxuXG4vKiogRXZlcnl0aGluZyB7QGxpbmsgZGVsaXZlclR1cm59IG5lZWRzIHRvIGFuc3dlciBvbmUgaW5ib3VuZCBhY3Rpdml0eS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGVsaXZlclR1cm5PcHRpb25zIHtcbiAgLyoqIFRoZSBhZ2VudCB0aGF0IGNvbXBvc2VzIHRoZSBjYXJkLiAqL1xuICBhZ2VudDogQ2FyZEFnZW50TGlrZTtcbiAgLyoqIFRoZSB2YWxpZGF0ZWQgaW5ib3VuZCBhY3Rpdml0eS4gKi9cbiAgYWN0aXZpdHk6IGFjdGl2aXR5Q29udHJhY3QuQWN0aXZpdHk7XG4gIC8qKiBCb3QgY3JlZGVudGlhbHMgdXNlZCB0byBmZXRjaCB0aGUgb3V0Ym91bmQgQ29ubmVjdG9yIHRva2VuLiAqL1xuICBjcmVkZW50aWFsczogQm90Q3JlZGVudGlhbHM7XG4gIC8qKlxuICAgKiBSZXBseSBkZXN0aW5hdGlvbiwgYWxyZWFkeSB2YWxpZGF0ZWQgYWdhaW5zdCB0aGUgaW5ib3VuZCB0b2tlbi4gUGFzc2luZyB0aGlzXG4gICAqIGV4cGxpY2l0bHkgKHJhdGhlciB0aGFuIHJlYWRpbmcgYGFjdGl2aXR5LnNlcnZpY2VVcmxgKSBrZWVwcyB0aGUgc2VjdXJpdHlcbiAgICogZGVjaXNpb24gaW4gdGhlIGNhbGxlciwgd2hlcmUgdGhlIHRva2VuIGlzIGluIHNjb3BlLlxuICAgKi9cbiAgc2VydmljZVVybDogc3RyaW5nO1xuICAvKipcbiAgICogQnVpbGRzIHRoZSBhZ2VudCdzIHBlci10dXJuIHJlcXVlc3QgY29udGV4dCwgc28gdGhlIGRlbGl2ZXJlZCB0dXJuIGhhcyB0aGVcbiAgICogc2FtZSB0b29sIHJlYWNoIChHZW5pZSBhbmQgZXZlcnkgb3RoZXIgdXNlci1zY29wZWQgdG9vbCkgYXMgYSBjaGF0IHR1cm4uXG4gICAqL1xuICBjcmVhdGVSZXF1ZXN0Q29udGV4dD86IENhcmRDb250ZXh0RmFjdG9yeTtcbiAgLyoqIENhbmNlbHMgdGhlIHR1cm4gKHByb2Nlc3Mgc2h1dGRvd24sIG9yIGEgdGVzdCB0ZWFyaW5nIGRvd24pLiAqL1xuICBzaWduYWw/OiBBYm9ydFNpZ25hbDtcbn1cblxuLyoqXG4gKiBSZWFkIHRoZSByZXBseSBkZXN0aW5hdGlvbiBvZmYgYW4gaW5ib3VuZCBhY3Rpdml0eSwgaWYgaXQgbmFtZXMgYSB1c2FibGUgb25lLlxuICpcbiAqIGBzZXJ2aWNlVXJsYCByaWRlcyBvbiB0aGUgYWN0aXZpdHkgYXMgYSBwbGFpbiBzdHJpbmcsIHNvIGl0IGlzIHZhbGlkYXRlZFxuICogYWdhaW5zdCB0aGUgdmVyaWZpZWQgdG9rZW4ncyBvd24gYHNlcnZpY2V1cmxgIGNsYWltIGJlZm9yZSBhbnl0aGluZ1xuICogYXV0aGVudGljYXRlZCBpcyBzZW50IHRoZXJlLlxuICovXG5leHBvcnQgY29uc3QgcmVzb2x2ZVNlcnZpY2VVcmwgPSAoXG4gIGFjdGl2aXR5OiBhY3Rpdml0eUNvbnRyYWN0LkFjdGl2aXR5LFxuICB0b2tlblNlcnZpY2VVcmw/OiBzdHJpbmcsXG4pOiBzdHJpbmcgfCBudWxsID0+IHtcbiAgY29uc3QgcmF3ID0gKGFjdGl2aXR5IGFzIHsgc2VydmljZVVybD86IHVua25vd24gfSkuc2VydmljZVVybDtcbiAgY29uc3QgY2FuZGlkYXRlID0gdHlwZW9mIHJhdyA9PT0gXCJzdHJpbmdcIiA/IHJhdy50cmltKCkgOiBcIlwiO1xuICBpZiAoIWNhbmRpZGF0ZSkgcmV0dXJuIG51bGw7XG4gIHJldHVybiBpc0FsbG93ZWRTZXJ2aWNlVXJsKGNhbmRpZGF0ZSwgdG9rZW5TZXJ2aWNlVXJsKSA/IGNhbmRpZGF0ZSA6IG51bGw7XG59O1xuXG4vKipcbiAqIFJ1biBvbmUgdHVybiBhbmQgZGVsaXZlciB0aGUgY2FyZCBiYWNrIHRocm91Z2ggdGhlIENvbm5lY3RvciBBUEkuXG4gKlxuICogQXdhaXRlZCBieSBub2JvZHkgb24gdGhlIHJlcXVlc3QgcGF0aCAodGhlIHJvdXRlIGhhcyBhbHJlYWR5IGFuc3dlcmVkIGAyMDBgKSxcbiAqIHNvIHRoaXMgb3ducyBpdHMgb3duIGVycm9yIGhhbmRsaW5nOiBhIGZhaWx1cmUgcG9zdHMge0BsaW5rIEZBSUxVUkVfVEVYVH0gdG9cbiAqIHRoZSBjaGFubmVsIGFuZCBpcyBsb2dnZWQsIG5ldmVyIHJldGhyb3duIGludG8gYW4gdW5oYW5kbGVkIHJlamVjdGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IGRlbGl2ZXJUdXJuID0gYXN5bmMgKG9wdGlvbnM6IERlbGl2ZXJUdXJuT3B0aW9ucyk6IFByb21pc2U8dm9pZD4gPT4ge1xuICBjb25zdCB7IGFnZW50LCBhY3Rpdml0eSwgY3JlZGVudGlhbHMsIHNlcnZpY2VVcmwgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IGNvbnZlcnNhdGlvbklkID0gYWN0aXZpdHkuY29udmVyc2F0aW9uPy5pZDtcbiAgaWYgKCFjb252ZXJzYXRpb25JZCkge1xuICAgIGxvZ2dlci53YXJuKFwiZHJvcHBpbmcgYWN0aXZpdHkgd2l0aCBubyBjb252ZXJzYXRpb24gaWRcIik7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgYmFzZSA9IHtcbiAgICBzZXJ2aWNlVXJsLFxuICAgIGNvbnZlcnNhdGlvbklkLFxuICAgIC4uLihhY3Rpdml0eS5pZCA/IHsgcmVwbHlUb0lkOiBhY3Rpdml0eS5pZCB9IDoge30pLFxuICAgIC4uLihvcHRpb25zLnNpZ25hbCA/IHsgc2lnbmFsOiBvcHRpb25zLnNpZ25hbCB9IDoge30pLFxuICB9O1xuXG4gIGxldCB0b2tlbjogc3RyaW5nO1xuICB0cnkge1xuICAgIHRva2VuID0gYXdhaXQgY29ubmVjdG9yVG9rZW4oe1xuICAgICAgYXBwSWQ6IGNyZWRlbnRpYWxzLmFwcElkLFxuICAgICAgYXBwUGFzc3dvcmQ6IGNyZWRlbnRpYWxzLmFwcFBhc3N3b3JkLFxuICAgICAgLi4uKGNyZWRlbnRpYWxzLmFwcFRlbmFudElkID8geyBhcHBUZW5hbnRJZDogY3JlZGVudGlhbHMuYXBwVGVuYW50SWQgfSA6IHt9KSxcbiAgICAgIC4uLihvcHRpb25zLnNpZ25hbCA/IHsgc2lnbmFsOiBvcHRpb25zLnNpZ25hbCB9IDoge30pLFxuICAgIH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICAvLyBObyB0b2tlbiBtZWFucyBub3RoaW5nIGNhbiBiZSBkZWxpdmVyZWQgLSBub3QgZXZlbiB0aGUgYXBvbG9neSAtIHNvIHRoaXNcbiAgICAvLyBpcyB0aGUgb25lIGZhaWx1cmUgdGhhdCBjYW4gb25seSBiZSBsb2dnZWQuXG4gICAgbG9nZ2VyLmVycm9yKFwiY291bGQgbm90IG9idGFpbiBhIGNvbm5lY3RvciB0b2tlblwiLCB7IGVycm9yOiBlcnJvci5lcnJvck1lc3NhZ2UoZXJyKSB9KTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCB0YXJnZXQgPSB7IC4uLmJhc2UsIHRva2VuIH07XG4gIGF3YWl0IHNlbmRUeXBpbmcodGFyZ2V0KTtcblxuICB0cnkge1xuICAgIGNvbnN0IGFjdGl2aXRpZXMgPSBhd2FpdCBydW5DYXJkVHVybihhZ2VudCwgYWN0aXZpdHksIHtcbiAgICAgIC4uLihvcHRpb25zLmNyZWF0ZVJlcXVlc3RDb250ZXh0XG4gICAgICAgID8geyBjcmVhdGVSZXF1ZXN0Q29udGV4dDogb3B0aW9ucy5jcmVhdGVSZXF1ZXN0Q29udGV4dCB9XG4gICAgICAgIDoge30pLFxuICAgICAgLi4uKG9wdGlvbnMuc2lnbmFsID8geyBzaWduYWw6IG9wdGlvbnMuc2lnbmFsIH0gOiB7fSksXG4gICAgfSk7XG4gICAgZm9yIChjb25zdCByZXBseSBvZiBhY3Rpdml0aWVzKSB7XG4gICAgICBhd2FpdCBzZW5kQWN0aXZpdHkocmVwbHksIHRhcmdldCk7XG4gICAgfVxuICAgIGxvZ2dlci5pbmZvKFwidHVybiBkZWxpdmVyZWRcIiwgeyBjb252ZXJzYXRpb246IGNvbnZlcnNhdGlvbklkLCByZXBsaWVzOiBhY3Rpdml0aWVzLmxlbmd0aCB9KTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nZ2VyLmVycm9yKFwidHVybiBmYWlsZWRcIiwgeyBjb252ZXJzYXRpb246IGNvbnZlcnNhdGlvbklkLCBlcnJvcjogZXJyb3IuZXJyb3JNZXNzYWdlKGVycikgfSk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHNlbmRBY3Rpdml0eSh7IHR5cGU6IFwibWVzc2FnZVwiLCB0ZXh0OiBGQUlMVVJFX1RFWFQgfSwgdGFyZ2V0KTtcbiAgICB9IGNhdGNoIChwb3N0RXJyKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoXCJjb3VsZCBub3QgcmVwb3J0IHRoZSBmYWlsdXJlIHRvIHRoZSBjaGFubmVsXCIsIHtcbiAgICAgICAgZXJyb3I6IGVycm9yLmVycm9yTWVzc2FnZShwb3N0RXJyKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufTtcbiJdfQ==
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppKit plugin (registered name: `teams`) that owns the Teams Adaptive Card
|
|
3
|
+
* runtime - the resolved card version and the optional incoming-webhook URL the
|
|
4
|
+
* {@link teamsCardTool} and the AppKit `teams.createCard` tool read. Registering
|
|
5
|
+
* it resolves and logs the effective config (which card version is in force,
|
|
6
|
+
* whether a webhook is wired up) so a misconfiguration is visible in the boot
|
|
7
|
+
* logs rather than on the first card, and installs the plugin's `execute()` as
|
|
8
|
+
* the runtime's executor so every build / post picks up AppKit's cache / retry /
|
|
9
|
+
* timeout / telemetry chain.
|
|
10
|
+
*
|
|
11
|
+
* The plugin is also a `ToolProvider`, so an AppKit agent can reach a
|
|
12
|
+
* `teams.createCard` tool directly; the {@link teamsCardTool} export is the same
|
|
13
|
+
* capability for a Mastra agent. Both share the runtime primed here.
|
|
14
|
+
*
|
|
15
|
+
* The plugin mounts four routes under its base path (`/api/teams`):
|
|
16
|
+
*
|
|
17
|
+
* - `POST /messages` is the REAL Microsoft Teams messaging endpoint - the URL
|
|
18
|
+
* an Azure Bot registration points at. It validates the Bot Service JWT,
|
|
19
|
+
* acknowledges immediately, and delivers the agent's card back over the
|
|
20
|
+
* Connector API. This is the route that makes a Teams channel able to chat
|
|
21
|
+
* with the app's agents, the same way the Mastra plugin exposes MCP at a
|
|
22
|
+
* path.
|
|
23
|
+
* - `POST /activity` runs the same turn SYNCHRONOUSLY, answering with the
|
|
24
|
+
* reply activities in the response body. It needs no bot registration, so
|
|
25
|
+
* it is what a local client (the in-repo preview chat), a test, or any
|
|
26
|
+
* non-Teams caller uses.
|
|
27
|
+
* - `POST /card` compiles a {@link card.CardSpec} into an Adaptive Card
|
|
28
|
+
* document (the preview page posts here).
|
|
29
|
+
* - `POST /post` pushes a compiled card to the configured Teams incoming
|
|
30
|
+
* webhook when one is set.
|
|
31
|
+
*
|
|
32
|
+
* Mirrors the node-email add-on's shape.
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
import { Plugin, type IAppRouter } from "@databricks/appkit";
|
|
37
|
+
import { type AgentToolDefinition, type ToolProvider } from "@databricks/appkit/beta";
|
|
38
|
+
import { card } from "@dbx-tools/shared-teams";
|
|
39
|
+
import { type TeamsPluginConfig } from "./config.js";
|
|
40
|
+
/**
|
|
41
|
+
* AppKit plugin that configures the Adaptive Card builder used by the
|
|
42
|
+
* `create_teams_card` tool, and exposes card building as an AppKit agent tool.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
47
|
+
* import { plugin as teamsPlugin } from "@dbx-tools/teams";
|
|
48
|
+
*
|
|
49
|
+
* await createApp({
|
|
50
|
+
* plugins: [
|
|
51
|
+
* server(),
|
|
52
|
+
* teamsPlugin.teams({ webhookUrl: process.env.TEAMS_WEBHOOK_URL }),
|
|
53
|
+
* ],
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare class TeamsPlugin extends Plugin<TeamsPluginConfig> implements ToolProvider {
|
|
58
|
+
static manifest: {
|
|
59
|
+
name: "teams";
|
|
60
|
+
displayName: string;
|
|
61
|
+
description: string;
|
|
62
|
+
stability: "beta";
|
|
63
|
+
resources: {
|
|
64
|
+
required: never[];
|
|
65
|
+
optional: never[];
|
|
66
|
+
};
|
|
67
|
+
config: {
|
|
68
|
+
schema: import("json-schema").JSONSchema7;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The tool this plugin offers to an AppKit agent.
|
|
73
|
+
*
|
|
74
|
+
* Marked `autoInheritable`: building a card is a pure, side-effect-free
|
|
75
|
+
* transform (nothing leaves the building), so it is safe to hand to any
|
|
76
|
+
* agent by default - unlike a send.
|
|
77
|
+
*
|
|
78
|
+
* `execute` re-parses its arguments with the local schema: AppKit validates
|
|
79
|
+
* against the same schema first, but re-parsing is what gives the body typed
|
|
80
|
+
* arguments instead of `unknown`.
|
|
81
|
+
*/
|
|
82
|
+
private readonly tools;
|
|
83
|
+
/**
|
|
84
|
+
* Prime the shared runtime from this plugin's config (over env), route the
|
|
85
|
+
* tool's builds through this plugin's interceptor chain, and log the
|
|
86
|
+
* effective config so the resolved card version and whether a webhook is
|
|
87
|
+
* wired up are obvious at boot.
|
|
88
|
+
*/
|
|
89
|
+
setup(): Promise<void>;
|
|
90
|
+
/** Drop the shared runtime. Idempotent. */
|
|
91
|
+
shutdown(): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Mount the card-building and card-posting routes under the plugin base
|
|
94
|
+
* path (`/api/teams`). `POST /card` is what the dev display page calls to
|
|
95
|
+
* preview a card live; `POST /post` pushes a compiled card to the configured
|
|
96
|
+
* Teams incoming webhook.
|
|
97
|
+
*
|
|
98
|
+
* Neither route is wrapped in `asUser(req)`: compiling a card is a pure
|
|
99
|
+
* transform of the request body and posting goes to a preconfigured webhook,
|
|
100
|
+
* so neither reads workspace data on the caller's behalf and neither needs an
|
|
101
|
+
* OBO token. Wrapping them would make the routes throw
|
|
102
|
+
* `AuthenticationError` whenever the user-token header is absent (a local
|
|
103
|
+
* `curl`, a health probe), which - since AppKit does not catch a rejection
|
|
104
|
+
* raised inside the handler - takes the process down rather than answering
|
|
105
|
+
* 401.
|
|
106
|
+
*/
|
|
107
|
+
injectRoutes(router: IAppRouter): void;
|
|
108
|
+
exports(): {
|
|
109
|
+
/**
|
|
110
|
+
* Compile a card spec into an Adaptive Card document. For agent-driven
|
|
111
|
+
* builds use {@link teamsCardTool} instead.
|
|
112
|
+
*/
|
|
113
|
+
buildCard: (spec: card.CardSpec, signal?: AbortSignal) => Promise<card.CardResult>;
|
|
114
|
+
/**
|
|
115
|
+
* Post a compiled card to the configured Teams incoming webhook. Throws
|
|
116
|
+
* when no webhook is configured.
|
|
117
|
+
*/
|
|
118
|
+
postCard: (cardDocument: card.AdaptiveCard, signal?: AbortSignal) => Promise<void>;
|
|
119
|
+
};
|
|
120
|
+
/** AppKit `ToolProvider`: the tool definitions offered to an agent. */
|
|
121
|
+
getAgentTools(): AgentToolDefinition[];
|
|
122
|
+
/**
|
|
123
|
+
* AppKit `ToolProvider`: run one tool call. Arguments are validated against
|
|
124
|
+
* the tool's schema first, and a validation failure comes back as an
|
|
125
|
+
* LLM-friendly string so the model can correct itself on the next turn.
|
|
126
|
+
*/
|
|
127
|
+
executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown>;
|
|
128
|
+
/**
|
|
129
|
+
* Handle one inbound request from Azure Bot Service on `POST /messages`.
|
|
130
|
+
*
|
|
131
|
+
* The order of operations is dictated by how Bot Service behaves, not by
|
|
132
|
+
* convenience:
|
|
133
|
+
*
|
|
134
|
+
* 1. **Refuse when unconfigured.** With no `appId` there is no audience to
|
|
135
|
+
* validate a token against, so the endpoint cannot be operated safely;
|
|
136
|
+
* it answers 503 rather than processing an unauthenticated activity.
|
|
137
|
+
* 2. **Validate the JWT before parsing the body.** The token is the only
|
|
138
|
+
* trust boundary this endpoint has.
|
|
139
|
+
* 3. **Pin the reply destination to the token.** `serviceUrl` arrives in the
|
|
140
|
+
* body, and replies carry the bot's credentials, so it is only honored
|
|
141
|
+
* when it matches the verified token.
|
|
142
|
+
* 4. **Answer 200 immediately, then run the agent.** Bot Service times out
|
|
143
|
+
* an unacknowledged activity in seconds and RETRIES it; a card takes far
|
|
144
|
+
* longer than that, and a retry would post a duplicate card.
|
|
145
|
+
*
|
|
146
|
+
* Activities that carry no prompt (`typing`, `conversationUpdate`, an
|
|
147
|
+
* attachment-only message) are acknowledged and dropped - exactly what a bot
|
|
148
|
+
* does with them.
|
|
149
|
+
*/
|
|
150
|
+
private handleMessage;
|
|
151
|
+
/** Compile a card, validating the request body against the spec schema. */
|
|
152
|
+
private executeBuild;
|
|
153
|
+
/**
|
|
154
|
+
* Run one conversation turn: validate the inbound activity, resolve the agent
|
|
155
|
+
* from the sibling agent plugin, and answer with card-carrying activities.
|
|
156
|
+
*
|
|
157
|
+
* Resolution failures are reported distinctly because they have different
|
|
158
|
+
* fixes: 503 when no agent plugin is mounted at all (a wiring problem), 404
|
|
159
|
+
* when the caller named an `agentId` that is not registered (a request
|
|
160
|
+
* problem).
|
|
161
|
+
*/
|
|
162
|
+
private executeTurn;
|
|
163
|
+
/**
|
|
164
|
+
* Compile then post a card, validating the request body against the spec
|
|
165
|
+
* schema. The compile is folded INTO the executed callback so a throw from
|
|
166
|
+
* either half (a build failure, or `postCard` refusing because no webhook is
|
|
167
|
+
* configured) comes back as a failed {@link ExecutionResult} the route can
|
|
168
|
+
* answer with, rather than escaping the handler as an unhandled rejection.
|
|
169
|
+
*/
|
|
170
|
+
private executePost;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Register the Teams plugin.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
178
|
+
* import { plugin as teamsPlugin } from "@dbx-tools/teams";
|
|
179
|
+
*
|
|
180
|
+
* await createApp({
|
|
181
|
+
* plugins: [server(), teamsPlugin.teams()],
|
|
182
|
+
* });
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export declare const teams: import("@databricks/appkit").ToPlugin<typeof TeamsPlugin, TeamsPluginConfig, "teams">;
|