@open-everywhere/provider-teams 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +46 -2
- package/agent/channels/teams-bot-name.ts +162 -0
- package/agent/channels/teams-catch-up.ts +244 -0
- package/agent/channels/teams-connector.ts +183 -0
- package/agent/channels/teams-conversation-ref.ts +398 -0
- package/agent/channels/teams-directory.ts +193 -0
- package/agent/channels/teams-engagement.ts +391 -0
- package/agent/channels/teams-fire-eligibility.ts +152 -0
- package/agent/channels/teams-front.ts +1404 -0
- package/agent/channels/teams-graph-boundary.ts +178 -0
- package/agent/channels/teams-graph-team-id.ts +106 -0
- package/agent/channels/teams-id-restore.ts +131 -0
- package/agent/channels/teams-identity.ts +395 -0
- package/agent/channels/teams-input-response.ts +129 -0
- package/agent/channels/teams-message-ops.ts +79 -0
- package/agent/channels/teams-origin-codec.ts +104 -0
- package/agent/channels/teams-post-arm.ts +323 -0
- package/agent/channels/teams-reaction-feedback.ts +127 -0
- package/agent/channels/teams-read.ts +510 -0
- package/agent/channels/teams-roster.ts +136 -0
- package/agent/channels/teams-stop.ts +319 -0
- package/agent/channels/teams-supervisor.ts +638 -0
- package/agent/channels/teams.ts +270 -0
- package/agent/contributions.ts +31 -0
- package/agent/docs-generated.ts +15 -0
- package/agent/ops.ts +129 -0
- package/agent/provider-events.ts +69 -0
- package/agent/register.ts +219 -0
- package/agent/surface-parity.ts +112 -0
- package/dist/agent/channels/teams-bot-name.d.ts +36 -0
- package/dist/agent/channels/teams-bot-name.d.ts.map +1 -0
- package/dist/agent/channels/teams-bot-name.js +141 -0
- package/dist/agent/channels/teams-bot-name.js.map +1 -0
- package/dist/agent/channels/teams-catch-up.d.ts +53 -0
- package/dist/agent/channels/teams-catch-up.d.ts.map +1 -0
- package/dist/agent/channels/teams-catch-up.js +221 -0
- package/dist/agent/channels/teams-catch-up.js.map +1 -0
- package/dist/agent/channels/teams-connector.d.ts +57 -0
- package/dist/agent/channels/teams-connector.d.ts.map +1 -0
- package/dist/agent/channels/teams-connector.js +132 -0
- package/dist/agent/channels/teams-connector.js.map +1 -0
- package/dist/agent/channels/teams-conversation-ref.d.ts +123 -0
- package/dist/agent/channels/teams-conversation-ref.d.ts.map +1 -0
- package/dist/agent/channels/teams-conversation-ref.js +368 -0
- package/dist/agent/channels/teams-conversation-ref.js.map +1 -0
- package/dist/agent/channels/teams-directory.d.ts +11 -0
- package/dist/agent/channels/teams-directory.d.ts.map +1 -0
- package/dist/agent/channels/teams-directory.js +179 -0
- package/dist/agent/channels/teams-directory.js.map +1 -0
- package/dist/agent/channels/teams-engagement.d.ts +174 -0
- package/dist/agent/channels/teams-engagement.d.ts.map +1 -0
- package/dist/agent/channels/teams-engagement.js +336 -0
- package/dist/agent/channels/teams-engagement.js.map +1 -0
- package/dist/agent/channels/teams-fire-eligibility.d.ts +26 -0
- package/dist/agent/channels/teams-fire-eligibility.d.ts.map +1 -0
- package/dist/agent/channels/teams-fire-eligibility.js +126 -0
- package/dist/agent/channels/teams-fire-eligibility.js.map +1 -0
- package/dist/agent/channels/teams-front.d.ts +249 -0
- package/dist/agent/channels/teams-front.d.ts.map +1 -0
- package/dist/agent/channels/teams-front.js +1143 -0
- package/dist/agent/channels/teams-front.js.map +1 -0
- package/dist/agent/channels/teams-graph-boundary.d.ts +45 -0
- package/dist/agent/channels/teams-graph-boundary.d.ts.map +1 -0
- package/dist/agent/channels/teams-graph-boundary.js +168 -0
- package/dist/agent/channels/teams-graph-boundary.js.map +1 -0
- package/dist/agent/channels/teams-graph-team-id.d.ts +30 -0
- package/dist/agent/channels/teams-graph-team-id.d.ts.map +1 -0
- package/dist/agent/channels/teams-graph-team-id.js +89 -0
- package/dist/agent/channels/teams-graph-team-id.js.map +1 -0
- package/dist/agent/channels/teams-id-restore.d.ts +34 -0
- package/dist/agent/channels/teams-id-restore.d.ts.map +1 -0
- package/dist/agent/channels/teams-id-restore.js +123 -0
- package/dist/agent/channels/teams-id-restore.js.map +1 -0
- package/dist/agent/channels/teams-identity.d.ts +148 -0
- package/dist/agent/channels/teams-identity.d.ts.map +1 -0
- package/dist/agent/channels/teams-identity.js +336 -0
- package/dist/agent/channels/teams-identity.js.map +1 -0
- package/dist/agent/channels/teams-input-response.d.ts +23 -0
- package/dist/agent/channels/teams-input-response.d.ts.map +1 -0
- package/dist/agent/channels/teams-input-response.js +102 -0
- package/dist/agent/channels/teams-input-response.js.map +1 -0
- package/dist/agent/channels/teams-message-ops.d.ts +3 -0
- package/dist/agent/channels/teams-message-ops.d.ts.map +1 -0
- package/dist/agent/channels/teams-message-ops.js +72 -0
- package/dist/agent/channels/teams-message-ops.js.map +1 -0
- package/dist/agent/channels/teams-origin-codec.d.ts +16 -0
- package/dist/agent/channels/teams-origin-codec.d.ts.map +1 -0
- package/dist/agent/channels/teams-origin-codec.js +99 -0
- package/dist/agent/channels/teams-origin-codec.js.map +1 -0
- package/dist/agent/channels/teams-post-arm.d.ts +37 -0
- package/dist/agent/channels/teams-post-arm.d.ts.map +1 -0
- package/dist/agent/channels/teams-post-arm.js +280 -0
- package/dist/agent/channels/teams-post-arm.js.map +1 -0
- package/dist/agent/channels/teams-reaction-feedback.d.ts +22 -0
- package/dist/agent/channels/teams-reaction-feedback.d.ts.map +1 -0
- package/dist/agent/channels/teams-reaction-feedback.js +108 -0
- package/dist/agent/channels/teams-reaction-feedback.js.map +1 -0
- package/dist/agent/channels/teams-read.d.ts +127 -0
- package/dist/agent/channels/teams-read.d.ts.map +1 -0
- package/dist/agent/channels/teams-read.js +424 -0
- package/dist/agent/channels/teams-read.js.map +1 -0
- package/dist/agent/channels/teams-roster.d.ts +54 -0
- package/dist/agent/channels/teams-roster.d.ts.map +1 -0
- package/dist/agent/channels/teams-roster.js +121 -0
- package/dist/agent/channels/teams-roster.js.map +1 -0
- package/dist/agent/channels/teams-stop.d.ts +74 -0
- package/dist/agent/channels/teams-stop.d.ts.map +1 -0
- package/dist/agent/channels/teams-stop.js +252 -0
- package/dist/agent/channels/teams-stop.js.map +1 -0
- package/dist/agent/channels/teams-supervisor.d.ts +351 -0
- package/dist/agent/channels/teams-supervisor.d.ts.map +1 -0
- package/dist/agent/channels/teams-supervisor.js +366 -0
- package/dist/agent/channels/teams-supervisor.js.map +1 -0
- package/dist/agent/channels/teams.d.ts +5 -0
- package/dist/agent/channels/teams.d.ts.map +1 -0
- package/dist/agent/channels/teams.js +246 -0
- package/dist/agent/channels/teams.js.map +1 -0
- package/dist/agent/contributions.d.ts +12 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +31 -0
- package/dist/agent/contributions.js.map +1 -0
- package/dist/agent/docs-generated.d.ts +3 -0
- package/dist/agent/docs-generated.d.ts.map +1 -0
- package/dist/agent/docs-generated.js +13 -0
- package/dist/agent/docs-generated.js.map +1 -0
- package/dist/agent/ops.d.ts +14 -0
- package/dist/agent/ops.d.ts.map +1 -0
- package/dist/agent/ops.js +122 -0
- package/dist/agent/ops.js.map +1 -0
- package/dist/agent/provider-events.d.ts +3 -0
- package/dist/agent/provider-events.d.ts.map +1 -0
- package/dist/agent/provider-events.js +67 -0
- package/dist/agent/provider-events.js.map +1 -0
- package/dist/agent/register.d.ts +16 -0
- package/dist/agent/register.d.ts.map +1 -0
- package/dist/agent/register.js +198 -0
- package/dist/agent/register.js.map +1 -0
- package/dist/agent/surface-parity.d.ts +3 -0
- package/dist/agent/surface-parity.d.ts.map +1 -0
- package/dist/agent/surface-parity.js +106 -0
- package/dist/agent/surface-parity.js.map +1 -0
- package/package.json +183 -4
- package/tsconfig.build.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-input-response.js","sourceRoot":"","sources":["../../../agent/channels/teams-input-response.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-message-ops.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-message-ops.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAyBpF,eAAO,MAAM,eAAe,EAAE,iBAwB7B,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// teams-message-ops — Teams's SurfaceMessageOps (OPE-899): the ancillary message ops core features
|
|
2
|
+
// dispatch through channels/surface-message-ops.ts. Before this file, Teams registered none, so the
|
|
3
|
+
// stop acknowledgement (turn-anchor.ts's `postSurfaceText`) resolved nothing on a Teams turn — the
|
|
4
|
+
// stop control worked (teams-stop.ts, the `angry` reaction), it was just invisible: a person who
|
|
5
|
+
// reacted saw the agent go quiet and read nothing.
|
|
6
|
+
//
|
|
7
|
+
// - postText → the SAME Bot-Framework send/reply the mid-turn post arm uses
|
|
8
|
+
// (teams-post-arm.ts's `dispatchTeamsReply`, exported for this file — one
|
|
9
|
+
// mint/send implementation, never a second copy).
|
|
10
|
+
// - editMessage → the SAME update-activity call `edit_reply` performs (`editTeamsMessage`).
|
|
11
|
+
// - NO `postFiles` (register.ts already records: "NO attachment resolver — the Teams surface
|
|
12
|
+
// carries no attachment plane today"). The optional tier exists exactly for this shape.
|
|
13
|
+
// - NO `setReaction` (register.ts: "NO reaction surface — Teams has no integration reaction
|
|
14
|
+
// primitive"). The `angry` reaction (teams-stop.ts) IS Teams's stop affordance; there is no
|
|
15
|
+
// second glyph to place beside it.
|
|
16
|
+
//
|
|
17
|
+
// THE PROBLEM THIS FILE SOLVES, IN ONE SENTENCE: `SurfaceMessageOps.postText`/`editMessage` are
|
|
18
|
+
// handed only a bare `channelId` (core's `channelId` is Teams's conversation id — see
|
|
19
|
+
// `turn-anchor.ts`'s `turnAnchorAddress`), never a `serviceUrl`, and a Bot-Framework write needs
|
|
20
|
+
// BOTH. `teams-conversation-ref.ts`'s `conversation`-grain address (OPE-899's addition to that
|
|
21
|
+
// module) is the lookup that answers it: every inbound activity, on every scope, stamps a row keyed
|
|
22
|
+
// on its EXACT conversation id, so the reply chain a channel turn anchored on is resolvable here by
|
|
23
|
+
// that same id.
|
|
24
|
+
//
|
|
25
|
+
// `opts.replyToMessageId` on `postText` threads the post via `replyToActivityId` — the SAME field
|
|
26
|
+
// the mid-turn reply arm anchors on. `turn-anchor.ts`'s own cancel-note call passes no such option
|
|
27
|
+
// (a top-level post, like every other surface's cancel note today), so this is exercised by other
|
|
28
|
+
// SurfaceMessageOps consumers (the approval decision notice), not by the stop path itself.
|
|
29
|
+
import { dispatchTeamsReply, editTeamsMessage } from "./teams-post-arm.js";
|
|
30
|
+
import { teamsConversationRef } from "./teams-conversation-ref.js";
|
|
31
|
+
import { restoreTeamsConversationId } from "./teams-id-restore.js";
|
|
32
|
+
/**
|
|
33
|
+
* The stored `conversation`-grain reference for `channelId`, or a loud, honest refusal — never a
|
|
34
|
+
* guessed target. Mirrors `github-message-ops.ts`'s `requireContainer`.
|
|
35
|
+
*
|
|
36
|
+
* 🔴 IT RESTORES THE RAW ID FIRST (OPE-1078). `channelId` reaches here in one of two spellings: raw,
|
|
37
|
+
* from a live turn's own anchor, or the SURFACE-KEY spelling, from a granted `teams:channel/<id>`
|
|
38
|
+
* address — the only spelling the resource-grant grammar admits. `restoreTeamsConversationId` is the
|
|
39
|
+
* identity on the first and the index lookup on the second, so both land on the same reference. The
|
|
40
|
+
* refusal below still names the id the CALLER gave, which is the one an operator can act on.
|
|
41
|
+
*/
|
|
42
|
+
async function requireConversationRef(channelId) {
|
|
43
|
+
const ref = await teamsConversationRef(await restoreTeamsConversationId(channelId));
|
|
44
|
+
if (!ref) {
|
|
45
|
+
throw new Error(`Teams surface message ops: no conversation reference for "${channelId}" — this deployment has never seen an inbound activity on that id.`);
|
|
46
|
+
}
|
|
47
|
+
return ref;
|
|
48
|
+
}
|
|
49
|
+
export const teamsMessageOps = {
|
|
50
|
+
platform: "teams",
|
|
51
|
+
/** Post through the mid-turn reply transport, resolving `serviceUrl` from the stored conversation
|
|
52
|
+
* reference. `opts?.replyToMessageId` threads the post — see the header. */
|
|
53
|
+
async postText(channelId, text, opts) {
|
|
54
|
+
const { serviceUrl, conversationId } = await requireConversationRef(channelId);
|
|
55
|
+
const target = {
|
|
56
|
+
platform: "teams",
|
|
57
|
+
serviceUrl,
|
|
58
|
+
conversationId,
|
|
59
|
+
...(opts?.replyToMessageId ? { replyToActivityId: opts.replyToMessageId } : {}),
|
|
60
|
+
};
|
|
61
|
+
return dispatchTeamsReply(target, text);
|
|
62
|
+
},
|
|
63
|
+
/** Update the given activity in place — the same transport `edit_reply` performs on the reply
|
|
64
|
+
* spine, resolving `serviceUrl` the same way `postText` does. It sends the RESOLVED conversation
|
|
65
|
+
* id, never the caller's, for the reason `requireConversationRef` states: the caller's may be the
|
|
66
|
+
* surface-key spelling, and the Connector accepts only the raw id. */
|
|
67
|
+
async editMessage(channelId, messageId, text) {
|
|
68
|
+
const { serviceUrl, conversationId } = await requireConversationRef(channelId);
|
|
69
|
+
await editTeamsMessage({ platform: "teams", channelId: conversationId, serviceUrl }, messageId, text);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=teams-message-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-message-ops.js","sourceRoot":"","sources":["../../../agent/channels/teams-message-ops.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,oGAAoG;AACpG,mGAAmG;AACnG,iGAAiG;AACjG,mDAAmD;AACnD,EAAE;AACF,iFAAiF;AACjF,4FAA4F;AAC5F,oEAAoE;AACpE,8FAA8F;AAC9F,+FAA+F;AAC/F,4FAA4F;AAC5F,8FAA8F;AAC9F,gGAAgG;AAChG,uCAAuC;AACvC,EAAE;AACF,gGAAgG;AAChG,sFAAsF;AACtF,iGAAiG;AACjG,+FAA+F;AAC/F,oGAAoG;AACpG,oGAAoG;AACpG,gBAAgB;AAChB,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,kGAAkG;AAClG,2FAA2F;AAG3F,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAoB,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE;;;;;;;;;GASG;AACH,KAAK,UAAU,sBAAsB,CAAC,SAAiB;IACrD,MAAM,GAAG,GAAG,MAAM,oBAAoB,CAAC,MAAM,0BAA0B,CAAC,SAAS,CAAC,CAAC,CAAC;IACpF,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,6DAA6D,SAAS,oEAAoE,CAC3I,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,QAAQ,EAAE,OAAO;IAEjB;iFAC6E;IAC7E,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI;QAClC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAgB;YAC1B,QAAQ,EAAE,OAAO;YACjB,UAAU;YACV,cAAc;YACd,GAAG,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChF,CAAC;QACF,OAAO,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;2EAGuE;IACvE,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI;QAC1C,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC/E,MAAM,gBAAgB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACxG,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UnifiedWakeOrigin, WakeOriginCodec } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
type TeamsOrigin = Extract<UnifiedWakeOrigin, {
|
|
3
|
+
platform: "teams";
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* Bot Connector `serviceUrl` host allowlist. The Teams reply POSTs a `client_credentials` bearer
|
|
7
|
+
* token to `${serviceUrl}/v3/conversations/...`, so the host must be a real Microsoft Bot
|
|
8
|
+
* Framework endpoint — not merely https. `serviceUrl` rides in off the inbound activity BODY, and
|
|
9
|
+
* eve's JWT verifier does NOT bind it to the token's `serviceurl` claim, so a scheme-only check
|
|
10
|
+
* (`startsWith("https://")`) would let a body-supplied `https://evil.example` exfiltrate the
|
|
11
|
+
* token. Constrain the host to the documented Bot Connector set. Fail-closed on any parse error.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isTrustedTeamsServiceUrl(serviceUrl: unknown): boolean;
|
|
14
|
+
export declare const teamsWakeOriginCodec: WakeOriginCodec<TeamsOrigin>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=teams-origin-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-origin-codec.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-origin-codec.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAErG,KAAK,WAAW,GAAG,OAAO,CAAC,iBAAiB,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAOrE;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAYrE;AA0BD,eAAO,MAAM,oBAAoB,EAAE,eAAe,CAAC,WAAW,CA8B7D,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// The Teams UnifiedWakeOrigin codec + the Bot-Connector serviceUrl host allowlist — MOVED VERBATIM
|
|
2
|
+
// out of core's channels/wake-origin-codecs.ts (OPE-114 stage 4). STORED-DATA COMPATIBILITY IS THE
|
|
3
|
+
// CONTRACT: a Teams origin is JSON-serialised into the durable operational store and fail-closed
|
|
4
|
+
// re-validated on read, so every behaviour here is frozen by core's
|
|
5
|
+
// scripts/wake-origin-fixtures-test.ts (run from the app root with this bundle's boot wiring —
|
|
6
|
+
// scripts/wake-origin-fixtures-boot.ts). Validator tolerances (typeof-only optional checks, extra
|
|
7
|
+
// unknown fields) are LOAD-BEARING for rows already on disk — never tighten them here.
|
|
8
|
+
//
|
|
9
|
+
// The `UnifiedWakeOrigin` TYPE union (including its teams arm) stays in core — it is the wire
|
|
10
|
+
// schema for stored rows; only the codec BEHAVIOUR lives in the bundle, registered by
|
|
11
|
+
// registerTeamsProvider() (../register.ts). `isTrustedTeamsServiceUrl` is exported for the teams
|
|
12
|
+
// post arm (BOTH of its dispatch paths re-validate the serviceUrl before POSTing the Connector
|
|
13
|
+
// token) and applied here on the stored-row `validate` arm — every route by which a serviceUrl can
|
|
14
|
+
// reach a token POST passes it. (There was a third route, the `dmOrigin` DM-delegation-anchor mint
|
|
15
|
+
// arm, which applied the same allowlist; it went with the anchor at OPE-368 — a removed route needs
|
|
16
|
+
// no check.) Core no longer re-exports the predicate (the stale unified-window re-export was removed
|
|
17
|
+
// in stage 4).
|
|
18
|
+
/** COLON-FREE by construction: a task windowId is `[^:]+` (core channels/window-key.ts), so any colon
|
|
19
|
+
* in a raw surface id (Teams' `19:...@thread.v2;messageid=` conversation ids) is folded to `_` —
|
|
20
|
+
* deterministic, so spawn + ingress still agree on identity. */
|
|
21
|
+
const noColon = (s) => s.replace(/:/g, "_");
|
|
22
|
+
/**
|
|
23
|
+
* Bot Connector `serviceUrl` host allowlist. The Teams reply POSTs a `client_credentials` bearer
|
|
24
|
+
* token to `${serviceUrl}/v3/conversations/...`, so the host must be a real Microsoft Bot
|
|
25
|
+
* Framework endpoint — not merely https. `serviceUrl` rides in off the inbound activity BODY, and
|
|
26
|
+
* eve's JWT verifier does NOT bind it to the token's `serviceurl` claim, so a scheme-only check
|
|
27
|
+
* (`startsWith("https://")`) would let a body-supplied `https://evil.example` exfiltrate the
|
|
28
|
+
* token. Constrain the host to the documented Bot Connector set. Fail-closed on any parse error.
|
|
29
|
+
*/
|
|
30
|
+
export function isTrustedTeamsServiceUrl(serviceUrl) {
|
|
31
|
+
if (typeof serviceUrl !== "string")
|
|
32
|
+
return false;
|
|
33
|
+
let host;
|
|
34
|
+
try {
|
|
35
|
+
const u = new URL(serviceUrl);
|
|
36
|
+
if (u.protocol !== "https:")
|
|
37
|
+
return false;
|
|
38
|
+
host = u.hostname.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// An entry beginning with "." is a dot-anchored suffix; otherwise an exact host.
|
|
44
|
+
return teamsServiceHostAllowlist().some((h) => (h.startsWith(".") ? host.endsWith(h) : host === h));
|
|
45
|
+
}
|
|
46
|
+
/** The GLOBAL (commercial) cloud Bot Connector hosts. Sovereign/national clouds use DIFFERENT
|
|
47
|
+
* hosts and are NOT in the default (keeping the default minimal, per the no-extrapolation rule):
|
|
48
|
+
* a GCC / GCC High / DoD / 21Vianet operator opts theirs in via OE_TEAMS_SERVICE_HOSTS. Verified
|
|
49
|
+
* values (Microsoft Learn, "Send proactive messages"): GCC `smba.infra.gcc.teams.microsoft.com`,
|
|
50
|
+
* GCC High `smba.infra.gov.teams.microsoft.us`, DoD `smba.infra.dod.teams.microsoft.us`. */
|
|
51
|
+
const DEFAULT_TEAMS_SERVICE_HOSTS = [
|
|
52
|
+
"smba.trafficmanager.net",
|
|
53
|
+
".smba.trafficmanager.net",
|
|
54
|
+
"botframework.com",
|
|
55
|
+
".botframework.com",
|
|
56
|
+
];
|
|
57
|
+
/** The effective serviceUrl host allowlist = the global default PLUS any operator-supplied
|
|
58
|
+
* OE_TEAMS_SERVICE_HOSTS (comma-separated; each entry an exact host or a ".suffix"). This is how
|
|
59
|
+
* a sovereign-cloud deployment admits its own Bot Connector host without weakening the default —
|
|
60
|
+
* the reply's bearer token only ever POSTs to a MATCHED host, all Microsoft-owned. */
|
|
61
|
+
function teamsServiceHostAllowlist() {
|
|
62
|
+
const extra = (process.env.OE_TEAMS_SERVICE_HOSTS ?? "")
|
|
63
|
+
.split(",")
|
|
64
|
+
.map((s) => s.trim().toLowerCase())
|
|
65
|
+
.filter((s) => s.length > 0);
|
|
66
|
+
return extra.length > 0 ? [...DEFAULT_TEAMS_SERVICE_HOSTS, ...extra] : DEFAULT_TEAMS_SERVICE_HOSTS;
|
|
67
|
+
}
|
|
68
|
+
export const teamsWakeOriginCodec = {
|
|
69
|
+
validate: (o) =>
|
|
70
|
+
// The Bot Connector bearer token is POSTed to serviceUrl, so it must be a Microsoft Bot
|
|
71
|
+
// Framework host, not merely https (isTrustedTeamsServiceUrl) — the reply path re-validates
|
|
72
|
+
// regardless, but a bad host must never survive into the map in the first place.
|
|
73
|
+
isTrustedTeamsServiceUrl(o.serviceUrl) &&
|
|
74
|
+
typeof o.conversationId === "string" &&
|
|
75
|
+
o.conversationId.length > 0 &&
|
|
76
|
+
(o.replyToActivityId === undefined || typeof o.replyToActivityId === "string") &&
|
|
77
|
+
(o.tenantId === undefined || typeof o.tenantId === "string"),
|
|
78
|
+
threadRef: (origin) => ({ channelKey: noColon(`teams/${origin.conversationId}`), threadKey: noColon(origin.replyToActivityId ?? origin.conversationId) }),
|
|
79
|
+
// replyToActivityId present ⇔ the trigger sits in a reply chain (the chain IS the thread);
|
|
80
|
+
// absent = a plain conversation send.
|
|
81
|
+
isThreadScoped: (origin) => typeof origin.replyToActivityId === "string" && origin.replyToActivityId.length > 0,
|
|
82
|
+
containerId: (origin) => origin.conversationId,
|
|
83
|
+
// A `dmOrigin` arm lived here (OPE-306 seam, landed OPE-315) to mint a DM delegation anchor's reply
|
|
84
|
+
// origin from the binding's `target` (`serviceUrl` + `conversationId`). OPE-368 deleted the anchor
|
|
85
|
+
// and the interface member with it — a Teams personal/group chat now diverts into its own
|
|
86
|
+
// first-class window (OPE-357), so its origin is minted and STORED at dispatch time like every other
|
|
87
|
+
// wake. NOTHING WEAKENED BY THE REMOVAL: the serviceUrl host allowlist that arm applied at mint is
|
|
88
|
+
// ALSO applied by `validate` above (every stored row) and again by the post arm before any Connector
|
|
89
|
+
// token leaves the process, so `isTrustedTeamsServiceUrl` remains on every route to a token POST.
|
|
90
|
+
//
|
|
91
|
+
// NO `placementRef` AND NO `carryPlacement`, AND THAT ABSENCE IS THE DECLARATION (OPE-1826). Teams
|
|
92
|
+
// keeps its placement INSIDE the conversation identity: `threadRef` keys on `replyToActivityId`,
|
|
93
|
+
// which is the very activity a reply hangs under, so two origins that pass `sameUnifiedConversation`
|
|
94
|
+
// already agree about where the answer lands and there is no gap for a carry to fill. Declaring an
|
|
95
|
+
// axis here would be worse than absent — it would make two origins of ONE thread compare unequal on
|
|
96
|
+
// a field the thread key has already settled. The position is recorded, and held to account against
|
|
97
|
+
// the live registry, by the `origin-placement-carry` row in core's identity/surface-parity.ts.
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=teams-origin-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-origin-codec.js","sourceRoot":"","sources":["../../../agent/channels/teams-origin-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PostArm, ReplyTarget, EditLocus } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
/** Exported for `teams-message-ops.ts` (OPE-899) — the SurfaceMessageOps registration reuses this
|
|
3
|
+
* exact shape (serviceUrl + conversationId + an optional reply anchor) rather than inventing a
|
|
4
|
+
* second one; see `dispatchTeamsReply`'s own export note. */
|
|
5
|
+
export type TeamsTarget = Extract<ReplyTarget, {
|
|
6
|
+
platform: "teams";
|
|
7
|
+
}>;
|
|
8
|
+
/** MID-TURN: a Bot-Framework message activity into the captured conversation reference. eve mints the
|
|
9
|
+
* Connector token from MICROSOFT_APP_ID/PASSWORD; check the id for an honest early error. Thread onto
|
|
10
|
+
* the trigger's root activity when the caller resolved one (a channel/group reply chains via
|
|
11
|
+
* replyToTeamsActivity — eve's exact reply semantics, matching the unified arm and channel.thread.post);
|
|
12
|
+
* a plain conversation send otherwise (personal chats, or no root id). */
|
|
13
|
+
/** Exported (OPE-899) so `teams-message-ops.ts`'s SurfaceMessageOps `postText` can post through the
|
|
14
|
+
* SAME transport this mid-turn reply arm uses, rather than a second copy of the Connector send.
|
|
15
|
+
* That module resolves its own `TeamsTarget` from the `conversation`-grain address store
|
|
16
|
+
* (`teams-conversation-ref.ts`, OPE-899) instead of from a live binding, but the target shape and
|
|
17
|
+
* every refusal below are identical either way. */
|
|
18
|
+
export declare function dispatchTeamsReply(target: TeamsTarget, text: string): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* EDIT (OPE-793): rewrite one of our own posted activities via eve's `updateTeamsActivity` (the Bot
|
|
21
|
+
* Framework update-activity call), on the SAME transport and credentials the post arms use. The
|
|
22
|
+
* Teams client shows an "Edited" marker on the activity, so the change is visible in the room.
|
|
23
|
+
*
|
|
24
|
+
* THE SAME TRUSTED-HOST ALLOWLIST BOTH POST ARMS APPLY. This call POSTs a client_credentials
|
|
25
|
+
* Connector bearer token to `locus.serviceUrl`, so it must be a Bot Framework host — the identical
|
|
26
|
+
* check, for the identical reason, and its absence here would be the OPE-315 gap re-opened on a new
|
|
27
|
+
* route. The serviceUrl reaches this arm from the session binding or the frozen wake origin (never
|
|
28
|
+
* model input), and it is re-validated anyway.
|
|
29
|
+
*
|
|
30
|
+
* NO SPLIT, like the Discord arm: one activity is one activity, so text past Teams' own cap is
|
|
31
|
+
* refused rather than truncated. Ownership was decided upstream against the own-post ledger.
|
|
32
|
+
*/
|
|
33
|
+
/** Exported (OPE-899) so `teams-message-ops.ts`'s SurfaceMessageOps `editMessage` reuses this SAME
|
|
34
|
+
* update-activity transport rather than a second copy of it. */
|
|
35
|
+
export declare function editTeamsMessage(locus: EditLocus, messageId: string, text: string): Promise<void>;
|
|
36
|
+
export declare const teamsPostArm: PostArm;
|
|
37
|
+
//# sourceMappingURL=teams-post-arm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-post-arm.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-post-arm.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EACV,OAAO,EAEP,WAAW,EAGX,SAAS,EAGV,MAAM,4CAA4C,CAAC;AAEpD;;8DAE8D;AAC9D,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAGtE;;;;2EAI2E;AAC3E;;;;oDAIoD;AACpD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B3F;AA2CD;;;;;;;;;;;;;GAaG;AACH;iEACiE;AACjE,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCvG;AA8HD,eAAO,MAAM,YAAY,EAAE,OAe1B,CAAC"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// teams-post-arm — Teams's POST arm for the post-arms registry (OPE-114 stage 1c). Absorbs, VERBATIM,
|
|
2
|
+
// Teams's arms out of the three reply/delivery spines: reply-post.ts (resolveReplyTarget's teams case
|
|
3
|
+
// + dispatchReply's send/reply branch + the teams splitTeamsMessageText chunking used by
|
|
4
|
+
// postTerminalProse), unified-reply.ts (liveTeamsPost, incl. the trusted-serviceUrl re-validation),
|
|
5
|
+
// and unified-redrive.ts (the teams describe/container cases). The transport is eve's own Bot-Framework
|
|
6
|
+
// Connector token (minted from MICROSOFT_APP_ID/PASSWORD) over a HOST-SIDE Connector fetch — see
|
|
7
|
+
// teams-connector.ts's header for why the send/reply/update calls are no longer eve's own
|
|
8
|
+
// `sendTeamsActivity`/`replyToTeamsActivity`/`updateTeamsActivity` (OPE-1237: eve's own transport
|
|
9
|
+
// throws away the response headers, so there is no way to honour a 429's `Retry-After` by wrapping
|
|
10
|
+
// its return value).
|
|
11
|
+
//
|
|
12
|
+
// ONE DELIBERATE DIVERGENCE FROM "VERBATIM" (OPE-315): dispatchTeamsReply now applies the
|
|
13
|
+
// isTrustedTeamsServiceUrl host allowlist its unified sibling always did. That was a real gap, not a
|
|
14
|
+
// stylistic one — see the check's own comment.
|
|
15
|
+
import { resolveTeamsAppId, splitTeamsMessageText, TEAMS_MESSAGE_TEXT_MAX_LENGTH } from "eve/channels/teams";
|
|
16
|
+
import { teamsConnectorWrite, sendTeamsActivityBackedOff, replyToTeamsActivityBackedOff, updateTeamsActivityBackedOff, } from "./teams-connector.js";
|
|
17
|
+
import { isTrustedTeamsServiceUrl } from "./teams-origin-codec.js";
|
|
18
|
+
import { teamsPersonalConversationRef, teamsSoleTenantRegion, rememberTeamsPersonalConversation, } from "./teams-conversation-ref.js";
|
|
19
|
+
import { restoreTeamsPersonId } from "./teams-id-restore.js";
|
|
20
|
+
import { markNotApplied, carryFailureSignals } from "@open-everywhere/core/agent/adapter-api.js";
|
|
21
|
+
/** MID-TURN: a Bot-Framework message activity into the captured conversation reference. eve mints the
|
|
22
|
+
* Connector token from MICROSOFT_APP_ID/PASSWORD; check the id for an honest early error. Thread onto
|
|
23
|
+
* the trigger's root activity when the caller resolved one (a channel/group reply chains via
|
|
24
|
+
* replyToTeamsActivity — eve's exact reply semantics, matching the unified arm and channel.thread.post);
|
|
25
|
+
* a plain conversation send otherwise (personal chats, or no root id). */
|
|
26
|
+
/** Exported (OPE-899) so `teams-message-ops.ts`'s SurfaceMessageOps `postText` can post through the
|
|
27
|
+
* SAME transport this mid-turn reply arm uses, rather than a second copy of the Connector send.
|
|
28
|
+
* That module resolves its own `TeamsTarget` from the `conversation`-grain address store
|
|
29
|
+
* (`teams-conversation-ref.ts`, OPE-899) instead of from a live binding, but the target shape and
|
|
30
|
+
* every refusal below are identical either way. */
|
|
31
|
+
export async function dispatchTeamsReply(target, text) {
|
|
32
|
+
// Both pre-flight refusals are DEFINITIVE (OPE-821): the call never leaves this host, so nothing
|
|
33
|
+
// reached the room and the post spine may release its dedupe claim for an identical retry.
|
|
34
|
+
if (!process.env.MICROSOFT_APP_ID && !process.env.TEAMS_APP_ID) {
|
|
35
|
+
throw markNotApplied(new Error("reply couldn't post to Teams — MICROSOFT_APP_ID is not set."));
|
|
36
|
+
}
|
|
37
|
+
// 🔴 THE SAME HOST ALLOWLIST THE UNIFIED ARM APPLIES (OPE-315). This path POSTs a client_credentials
|
|
38
|
+
// Connector bearer token to `target.serviceUrl`, and until now it was the ONE serviceUrl route with
|
|
39
|
+
// no host check: the stored-origin path is gated by the codec validator (teams-origin-codec.ts) and
|
|
40
|
+
// re-gated in liveTeamsPost, but a mid-turn target is assembled by resolveTarget from binding
|
|
41
|
+
// attributes with only a non-empty test. Narrow (it takes a rogue Bot Framework channel on the same
|
|
42
|
+
// bot registration to place an attacker host there) but the same class as the unified arm's, and
|
|
43
|
+
// the fix is three lines. Token-free error, mirroring liveTeamsPost's.
|
|
44
|
+
if (!isTrustedTeamsServiceUrl(target.serviceUrl)) {
|
|
45
|
+
throw markNotApplied(new Error(`reply couldn't post to Teams — serviceUrl is not a Bot Framework host (conversation ${target.conversationId})`));
|
|
46
|
+
}
|
|
47
|
+
const res = target.replyToActivityId
|
|
48
|
+
? await replyToTeamsActivityBackedOff({
|
|
49
|
+
serviceUrl: target.serviceUrl,
|
|
50
|
+
conversationId: target.conversationId,
|
|
51
|
+
activityId: target.replyToActivityId,
|
|
52
|
+
body: { type: "message", text },
|
|
53
|
+
})
|
|
54
|
+
: await sendTeamsActivityBackedOff({
|
|
55
|
+
serviceUrl: target.serviceUrl,
|
|
56
|
+
conversationId: target.conversationId,
|
|
57
|
+
body: { type: "message", text },
|
|
58
|
+
});
|
|
59
|
+
return res.id;
|
|
60
|
+
}
|
|
61
|
+
/** UNIFIED: a Bot-Framework message activity into the frozen conversation reference — the SAME
|
|
62
|
+
* transport + env credentials the mid-turn reply arm uses. Anchored into the reply CHAIN via
|
|
63
|
+
* replyToTeamsActivity when the origin froze a thread-root activity id (teamsThreadRootActivityId),
|
|
64
|
+
* else a plain conversation send. Every target field rides the host-frozen wake origin, never the
|
|
65
|
+
* model. */
|
|
66
|
+
async function liveTeamsPost(origin, text) {
|
|
67
|
+
if (!process.env.MICROSOFT_APP_ID && !process.env.TEAMS_APP_ID) {
|
|
68
|
+
// DEFINITIVE, like the mid-turn arm's pre-flights: nothing left this host.
|
|
69
|
+
throw markNotApplied(new Error(`unified reply couldn't post to Teams — MICROSOFT_APP_ID is not set (conversation ${origin.conversationId})`));
|
|
70
|
+
}
|
|
71
|
+
// Belt-and-braces re-validation (isUnifiedWakeOrigin already enforces it): the Connector bearer
|
|
72
|
+
// token is POSTed to this host, so it must be a Microsoft Bot Framework endpoint — a scheme-only
|
|
73
|
+
// https check would let a body-supplied serviceUrl exfiltrate the token.
|
|
74
|
+
if (!isTrustedTeamsServiceUrl(origin.serviceUrl)) {
|
|
75
|
+
throw markNotApplied(new Error(`unified reply couldn't post to Teams — serviceUrl is not a Bot Framework host (conversation ${origin.conversationId})`));
|
|
76
|
+
}
|
|
77
|
+
let id;
|
|
78
|
+
try {
|
|
79
|
+
const res = origin.replyToActivityId
|
|
80
|
+
? await replyToTeamsActivityBackedOff({
|
|
81
|
+
serviceUrl: origin.serviceUrl,
|
|
82
|
+
conversationId: origin.conversationId,
|
|
83
|
+
activityId: origin.replyToActivityId,
|
|
84
|
+
body: { type: "message", text },
|
|
85
|
+
})
|
|
86
|
+
: await sendTeamsActivityBackedOff({
|
|
87
|
+
serviceUrl: origin.serviceUrl,
|
|
88
|
+
conversationId: origin.conversationId,
|
|
89
|
+
body: { type: "message", text },
|
|
90
|
+
});
|
|
91
|
+
id = res.id;
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// Token-free error: never echo the transport error body (defensive parity with the other arms).
|
|
95
|
+
// AMBIGUOUS unless the Connector gave a status to say otherwise (OPE-821) — a send whose answer
|
|
96
|
+
// never arrived may still have reached the room, so the class carries over rather than being
|
|
97
|
+
// dropped by the re-wrap. A number is the only thing carried; no body, no URL, no token.
|
|
98
|
+
throw carryFailureSignals(err, new Error(`unified reply couldn't post to Teams — the Connector post failed (conversation ${origin.conversationId})`));
|
|
99
|
+
}
|
|
100
|
+
return id || "unknown";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* EDIT (OPE-793): rewrite one of our own posted activities via eve's `updateTeamsActivity` (the Bot
|
|
104
|
+
* Framework update-activity call), on the SAME transport and credentials the post arms use. The
|
|
105
|
+
* Teams client shows an "Edited" marker on the activity, so the change is visible in the room.
|
|
106
|
+
*
|
|
107
|
+
* THE SAME TRUSTED-HOST ALLOWLIST BOTH POST ARMS APPLY. This call POSTs a client_credentials
|
|
108
|
+
* Connector bearer token to `locus.serviceUrl`, so it must be a Bot Framework host — the identical
|
|
109
|
+
* check, for the identical reason, and its absence here would be the OPE-315 gap re-opened on a new
|
|
110
|
+
* route. The serviceUrl reaches this arm from the session binding or the frozen wake origin (never
|
|
111
|
+
* model input), and it is re-validated anyway.
|
|
112
|
+
*
|
|
113
|
+
* NO SPLIT, like the Discord arm: one activity is one activity, so text past Teams' own cap is
|
|
114
|
+
* refused rather than truncated. Ownership was decided upstream against the own-post ledger.
|
|
115
|
+
*/
|
|
116
|
+
/** Exported (OPE-899) so `teams-message-ops.ts`'s SurfaceMessageOps `editMessage` reuses this SAME
|
|
117
|
+
* update-activity transport rather than a second copy of it. */
|
|
118
|
+
export async function editTeamsMessage(locus, messageId, text) {
|
|
119
|
+
// The three pre-flight refusals are DEFINITIVE (OPE-802): the call never leaves this host, so the
|
|
120
|
+
// message cannot have changed and a fresh `reply` is the right remedy. The Connector failure below
|
|
121
|
+
// is not — a failed update may still have applied — so it stays unmarked and fails closed.
|
|
122
|
+
if (!process.env.MICROSOFT_APP_ID && !process.env.TEAMS_APP_ID) {
|
|
123
|
+
throw markNotApplied(new Error("edit_reply couldn't update the Teams message — MICROSOFT_APP_ID is not set."));
|
|
124
|
+
}
|
|
125
|
+
if (!locus.serviceUrl || !isTrustedTeamsServiceUrl(locus.serviceUrl)) {
|
|
126
|
+
throw markNotApplied(new Error(`edit_reply couldn't update the Teams message — serviceUrl is not a Bot Framework host (conversation ${locus.channelId})`));
|
|
127
|
+
}
|
|
128
|
+
if (text.length > TEAMS_MESSAGE_TEXT_MAX_LENGTH) {
|
|
129
|
+
throw markNotApplied(new Error(`edit_reply couldn't update the Teams message — the new text is ${text.length} characters and Teams caps one message at ${TEAMS_MESSAGE_TEXT_MAX_LENGTH}. An edit cannot split a message; shorten it, or post a new reply.`));
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
await updateTeamsActivityBackedOff({
|
|
133
|
+
serviceUrl: locus.serviceUrl,
|
|
134
|
+
conversationId: locus.channelId,
|
|
135
|
+
activityId: messageId,
|
|
136
|
+
body: { type: "message", text },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
// Token-free error, matching both post arms: never echo the transport error body. `err` may now
|
|
141
|
+
// be the NAMED `RateLimitExhaustedError` (OPE-1237) rather than an ordinary Connector throw —
|
|
142
|
+
// `carryFailureSignals` copies its `status` (429, always AMBIGUOUS per platform-failure.ts) onto
|
|
143
|
+
// the wrapped error, the same treatment `liveTeamsPost`'s catch below gives it. That keeps the
|
|
144
|
+
// 429 case failing closed exactly as an unclassified Connector failure already did — this stays
|
|
145
|
+
// an improvement in what a caller reading `.status` can tell, never a behaviour change.
|
|
146
|
+
throw carryFailureSignals(err, new Error(`edit_reply couldn't update the Teams message — the Connector update failed (conversation ${locus.channelId})`));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Teams' own pre-flight: without an app id nothing can mint a Connector token. DEFINITIVE — the
|
|
150
|
+
* call never leaves this host — so the caller may retry an identical send once the id is set. */
|
|
151
|
+
function requireTeamsAppCredential(what) {
|
|
152
|
+
if (!process.env.MICROSOFT_APP_ID && !process.env.TEAMS_APP_ID) {
|
|
153
|
+
throw markNotApplied(new Error(`${what} couldn't reach Teams — MICROSOFT_APP_ID is not set.`));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Open (or re-open) the 1:1 chat between the bot and one person — `POST {serviceUrl}/v3/conversations`
|
|
158
|
+
* with the bot, the member and the tenant.
|
|
159
|
+
*
|
|
160
|
+
* IT IS THE ONLY WAY TO COLD-ADDRESS A TEAMS PERSON, and it is why OPE-887 exists: the Bot Framework
|
|
161
|
+
* has no `conversations.open` keyed on a bare user id, so the deployment must already know a REGION
|
|
162
|
+
* (`serviceUrl`) and a TENANT before it can ask. Both come from the proactive-address table
|
|
163
|
+
* (channels/teams-conversation-ref.ts), captured off real inbound activities.
|
|
164
|
+
*
|
|
165
|
+
* The create is IDEMPOTENT for a one-member, non-group conversation: Microsoft documents it as
|
|
166
|
+
* returning the EXISTING 1:1 chat rather than minting a second one. That is a documented fact and not
|
|
167
|
+
* a measured one — if it turned out to be false, the visible symptom would be a duplicate chat, never
|
|
168
|
+
* a lost message, and the stored reference below means the call happens at most once per person
|
|
169
|
+
* anyway.
|
|
170
|
+
*
|
|
171
|
+
* 🔴 THE REFUSAL THIS ARM MUST STAY HONEST ABOUT: Microsoft refuses the create when the Teams app is
|
|
172
|
+
* not installed for that person in the PERSONAL scope. So a Teams DM arm reaches the people who have
|
|
173
|
+
* the app, and NOT the whole tenant. The error names that, because "the message did not send" without
|
|
174
|
+
* the reason sends a caller looking in the wrong place.
|
|
175
|
+
*/
|
|
176
|
+
async function openTeamsPersonalConversation(region, principalId) {
|
|
177
|
+
const appId = await resolveTeamsAppId();
|
|
178
|
+
const res = await teamsConnectorWrite({
|
|
179
|
+
operation: "conversations.create",
|
|
180
|
+
serviceUrl: region.serviceUrl,
|
|
181
|
+
path: "/v3/conversations",
|
|
182
|
+
method: "POST",
|
|
183
|
+
body: {
|
|
184
|
+
isGroup: false,
|
|
185
|
+
bot: { id: `28:${appId}` },
|
|
186
|
+
members: [{ id: principalId }],
|
|
187
|
+
channelData: { tenant: { id: region.tenantId } },
|
|
188
|
+
tenantId: region.tenantId,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const id = res.ok && res.body && typeof res.body === "object" ? res.body.id : undefined;
|
|
192
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
193
|
+
// Token-free, and never the Connector's own body: the response can echo request material.
|
|
194
|
+
throw new Error(`couldn't open a Teams 1:1 chat with that account (Bot Framework answered ${res.status}). Microsoft refuses this when the Teams app is not installed for that person in the personal scope.`);
|
|
195
|
+
}
|
|
196
|
+
return id;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* DM (OPE-887): deliver `text` to one person's 1:1 Teams chat. The THIRD provider to implement this
|
|
200
|
+
* member, after Slack and Discord, and the one whose transport needed a durable seam first.
|
|
201
|
+
*
|
|
202
|
+
* WHAT IT COSTS TO GET HERE, in one paragraph. `DirectMessageRequest` names WHO and never WHERE, which
|
|
203
|
+
* is right and which Slack and Discord satisfy from a bot token alone. Teams cannot: a send needs a
|
|
204
|
+
* regional `serviceUrl` and a Microsoft-minted conversation id, and neither is derivable from a person
|
|
205
|
+
* id. So the deployment captures both off inbound activities (channels/teams-conversation-ref.ts) and
|
|
206
|
+
* this arm reads them back. The caller's contract is unchanged.
|
|
207
|
+
*
|
|
208
|
+
* THREE OUTCOMES, and each is a real answer:
|
|
209
|
+
* 1. a 1:1 reference is stored ⇒ one send.
|
|
210
|
+
* 2. no 1:1 reference, but this deployment knows exactly one tenant and its region ⇒ open the chat,
|
|
211
|
+
* remember it, send.
|
|
212
|
+
* 3. neither ⇒ REFUSE, loudly and token-free. That is not a defect to paper over: this deployment
|
|
213
|
+
* genuinely has no address for that person, and a silent no-op would leave a consent ask
|
|
214
|
+
* apparently sent and actually nowhere.
|
|
215
|
+
*
|
|
216
|
+
* `actions` ARE IGNORED, deliberately and per the registry's "undefined = unsupported, never faked":
|
|
217
|
+
* this arm posts a plain message activity, and a Teams button would need an Adaptive Card plus an
|
|
218
|
+
* invoke route to receive the tap. Core's copy always stands on its own without a button
|
|
219
|
+
* ({@link DirectMessageAction}'s own rule), so the text delivered here is complete.
|
|
220
|
+
*/
|
|
221
|
+
async function deliverTeamsDirectMessage(input) {
|
|
222
|
+
requireTeamsAppCredential("the direct message");
|
|
223
|
+
// 🔴 RESTORE THE RAW `29:` ID ONCE, HERE, BEFORE ANY USE (OPE-1078). A granted `teams:person/<id>`
|
|
224
|
+
// address carries the SURFACE-KEY spelling — the only one the resource-grant grammar admits — and
|
|
225
|
+
// every use below needs the raw Bot Framework id: the stored-reference lookup keys on it, and
|
|
226
|
+
// `openTeamsPersonalConversation` puts it in the Connector's `members` list, where a wrong spelling
|
|
227
|
+
// is a refused create rather than a delivered message. A caller that already holds the raw id (the
|
|
228
|
+
// recipient's own binding) is answered unchanged and pays no store read.
|
|
229
|
+
const principalId = await restoreTeamsPersonId(input.principalId.trim());
|
|
230
|
+
if (principalId.length === 0)
|
|
231
|
+
throw markNotApplied(new Error("the direct message couldn't reach Teams — no account id was given."));
|
|
232
|
+
const stored = await teamsPersonalConversationRef(principalId);
|
|
233
|
+
let ref;
|
|
234
|
+
if (stored) {
|
|
235
|
+
ref = stored;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const region = await teamsSoleTenantRegion();
|
|
239
|
+
if (!region) {
|
|
240
|
+
// DEFINITIVE: nothing left this host. Naming the two ways an address is acquired is what lets an
|
|
241
|
+
// operator act — the alternative is a message that never arrives and no reason why.
|
|
242
|
+
throw markNotApplied(new Error("the direct message couldn't reach Teams — this deployment holds no Bot Framework conversation reference for that account, and no single Teams tenant it could open one in. A reference is captured when the person messages the bot, or when the bot sees a channel in exactly one tenant."));
|
|
243
|
+
}
|
|
244
|
+
ref = { ...region, conversationId: await openTeamsPersonalConversation(region, principalId) };
|
|
245
|
+
// Best-effort: a failed write costs a repeated create next time, never a failed send.
|
|
246
|
+
await rememberTeamsPersonalConversation(principalId, ref);
|
|
247
|
+
}
|
|
248
|
+
// READ-TIME allowlist, for the same reason every other Teams write applies one: this POSTs a
|
|
249
|
+
// Connector bearer token to the host. The stored reference was checked on the way in too.
|
|
250
|
+
if (!isTrustedTeamsServiceUrl(ref.serviceUrl)) {
|
|
251
|
+
throw markNotApplied(new Error("the direct message couldn't reach Teams — the stored serviceUrl is not a Bot Framework host."));
|
|
252
|
+
}
|
|
253
|
+
// Teams caps one activity, so a long notice is split exactly as every other Teams post is; the
|
|
254
|
+
// FIRST part's id is the returned anchor, matching the Discord arm.
|
|
255
|
+
const parts = splitTeamsMessageText(input.text);
|
|
256
|
+
let messageId = "";
|
|
257
|
+
for (const part of parts) {
|
|
258
|
+
const res = await sendTeamsActivityBackedOff({ serviceUrl: ref.serviceUrl, conversationId: ref.conversationId, body: { type: "message", text: part } });
|
|
259
|
+
if (messageId.length === 0)
|
|
260
|
+
messageId = res.id;
|
|
261
|
+
}
|
|
262
|
+
return { surfaceId: ref.conversationId, messageId: messageId || "unknown" };
|
|
263
|
+
}
|
|
264
|
+
export const teamsPostArm = {
|
|
265
|
+
platform: "teams",
|
|
266
|
+
resolveTarget: (bag) => {
|
|
267
|
+
// Proactive Teams sends need the Bot-Framework conversation reference (serviceUrl + conversation
|
|
268
|
+
// id) the inbound turn captured — you cannot cold-address a Teams chat.
|
|
269
|
+
const serviceUrl = bag.str("serviceUrl");
|
|
270
|
+
const conversationId = bag.str("conversationId");
|
|
271
|
+
return serviceUrl && conversationId ? { platform: "teams", serviceUrl, conversationId } : null;
|
|
272
|
+
},
|
|
273
|
+
dispatchReply: (target, text) => dispatchTeamsReply(target, text),
|
|
274
|
+
dispatchUnified: (origin, text, deps) => (deps.postTeams ?? liveTeamsPost)(origin, text),
|
|
275
|
+
describeRedriveTarget: (origin) => `teams conversation ${origin.conversationId}`,
|
|
276
|
+
redriveContainerId: (origin) => origin.conversationId,
|
|
277
|
+
editMessage: editTeamsMessage,
|
|
278
|
+
deliverDirectMessage: deliverTeamsDirectMessage,
|
|
279
|
+
};
|
|
280
|
+
//# sourceMappingURL=teams-post-arm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-post-arm.js","sourceRoot":"","sources":["../../../agent/channels/teams-post-arm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReactionIngestOutcome, type ReactionSinkDeps } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
import type { TeamsReactionActivity } from "./teams-stop.js";
|
|
3
|
+
/** Injected seams, so parse + guard + record is unit-tested against stubs with no Bot registration. */
|
|
4
|
+
export interface TeamsReactionFeedbackDeps {
|
|
5
|
+
readonly sink: ReactionSinkDeps;
|
|
6
|
+
}
|
|
7
|
+
/** Production deps: the live audit/telemetry sink core already owns. There is no Teams-specific
|
|
8
|
+
* sink — the whole point of the seam is that the durable row is written in ONE place. */
|
|
9
|
+
export declare function liveTeamsReactionFeedbackDeps(): TeamsReactionFeedbackDeps;
|
|
10
|
+
/**
|
|
11
|
+
* Ingest every reaction one Teams `messageReaction` activity carries. Returns one outcome per
|
|
12
|
+
* reaction — added first, then removed — so a caller's log says what happened to each.
|
|
13
|
+
*
|
|
14
|
+
* It NEVER THROWS into the caller: core's sinks are best-effort by contract, and a lost feedback row
|
|
15
|
+
* must never cost the ingress its ack.
|
|
16
|
+
*
|
|
17
|
+
* An activity with nothing to ingest returns an EMPTY array. That is the honest answer for a bot's
|
|
18
|
+
* own reaction, an activity with no reactor, and an activity naming no conversation — none of the
|
|
19
|
+
* three is a reaction from a person on a message this deployment can key.
|
|
20
|
+
*/
|
|
21
|
+
export declare function routeTeamsReactionFeedback(rx: TeamsReactionActivity, deps: TeamsReactionFeedbackDeps): Promise<readonly ReactionIngestOutcome[]>;
|
|
22
|
+
//# sourceMappingURL=teams-reaction-feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-reaction-feedback.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-reaction-feedback.ts"],"names":[],"mappings":"AAmCA,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,uGAAuG;AACvG,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC;AAED;0FAC0F;AAC1F,wBAAgB,6BAA6B,IAAI,yBAAyB,CAEzE;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,qBAAqB,EACzB,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,SAAS,qBAAqB,EAAE,CAAC,CAiD3C"}
|