@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,126 @@
|
|
|
1
|
+
// teams-fire-eligibility — Microsoft Graph `accountEnabled` + channel-membership probes for the
|
|
2
|
+
// routine fire-eligibility leg (OPE-522), mirroring core's Slack-only predicate
|
|
3
|
+
// (@open-everywhere/core routines/fire-eligibility.ts, OPE-506) for Teams. Registered into core's
|
|
4
|
+
// teams-bridge.ts (channels/teams-bridge.ts) by registerTeamsProvider() — core stays free of
|
|
5
|
+
// Graph-specific parsing, the same split teams-read.ts already draws for channel reads.
|
|
6
|
+
//
|
|
7
|
+
// IDENTITY: both probes are keyed on the creator's Azure AD OBJECT id, never the Bot Framework
|
|
8
|
+
// `from.id` that `Routine.createdBy` carries for Teams — a DIFFERENT identifier (see
|
|
9
|
+
// teams-identity.ts's separate `aadObjectId` capture, and core's fire-eligibility.ts header). The
|
|
10
|
+
// caller (core's dispatch.ts) reads it back off the routine's stored target via
|
|
11
|
+
// register.ts's resolveTeamsTarget, which stamps it onto the fresh DispatchTarget's `attributes`.
|
|
12
|
+
//
|
|
13
|
+
// TOKEN + TRANSPORT: reuses teams-read.ts's `liveTeamsGraphRequest` (app-only client-credentials
|
|
14
|
+
// Graph token, warm-cached, single-flight) — these are ordinary Graph GETs, no different in shape
|
|
15
|
+
// from a channel-history read. `liveTeamsGraphRequest` fails LOUD (throws) on a missing token or a
|
|
16
|
+
// non-ok response; every probe below catches around it, because this module's contract is fail-OPEN
|
|
17
|
+
// on uncertainty, not fail-loud like the read adapter's.
|
|
18
|
+
//
|
|
19
|
+
// FAIL-OPEN ON UNCERTAINTY, same posture as every other probe in this module family
|
|
20
|
+
// (routines/fire-eligibility.ts, fire-creator.ts): only a well-formed Graph response carrying a
|
|
21
|
+
// POSITIVE "no" (`accountEnabled === false`, or a FULLY-ENUMERATED member list that does not include
|
|
22
|
+
// the creator) skips a fire. A network error, an auth failure, a malformed response shape, or an id
|
|
23
|
+
// Graph can't resolve is UNRESOLVABLE — never a fabricated "no". This is DELIBERATELY STRICTER than
|
|
24
|
+
// routines/fire-eligibility.ts's own Slack room-membership probe on one point: that probe's
|
|
25
|
+
// pagination bound (ROOM_MEMBER_MAX_PAGES) falls through to a cached `false` when a channel has more
|
|
26
|
+
// members than the bound can scan, which reads as a confirmed "not a member" even though the roster
|
|
27
|
+
// was never fully seen. This module tracks whether the LAST page fetched still had a `nextLink`
|
|
28
|
+
// (more roster left unseen) and reports `roster_incomplete` in that case — "hit the page bound with
|
|
29
|
+
// more to see" is kept distinct from "scanned every member and did not find them".
|
|
30
|
+
//
|
|
31
|
+
// 🔴 THE MEMBERSHIP READ TAKES THE GRAPH TEAM ID, AND THE ACTIVITY DOES NOT CARRY ONE (OPE-1810).
|
|
32
|
+
// A Teams activity carries the Bot Connector's team id, a `19:…@thread.tacv2` string; every Graph
|
|
33
|
+
// URL under `/teams/{team-id}/…` takes the team's Microsoft 365 GROUP id instead. Handing the
|
|
34
|
+
// Connector spelling to Graph addresses nothing and answers 404 — so before OPE-1810 this probe
|
|
35
|
+
// threw on EVERY call, read as unresolvable, and the creator check never once enforced. It now
|
|
36
|
+
// resolves the one into the other through channels/teams-graph-team-id.ts, the same call the read
|
|
37
|
+
// verbs and the catch-up window already make.
|
|
38
|
+
//
|
|
39
|
+
// 🔴 AND THE ANSWER NOW BINDS (Tommy, 2026-09-13). A creator confirmed OUT of the channel stops
|
|
40
|
+
// getting their routine, the same outcome and the same `creator_removed_from_room` audit row the
|
|
41
|
+
// Slack path has produced since OPE-506. Fail-open survives only for an answer the probe could not
|
|
42
|
+
// GET, and every such arm names its own cause ({@link TeamsMembershipUnresolved}) so the dispatcher
|
|
43
|
+
// can audit it — a fail-open that writes nothing is indistinguishable from a pass, which is the
|
|
44
|
+
// state this module was in for its whole life before this ticket.
|
|
45
|
+
import { teamsGraphTeamId } from "./teams-graph-team-id.js";
|
|
46
|
+
import { liveTeamsGraphRequest } from "./teams-read.js";
|
|
47
|
+
const GRAPH_BASE = "https://graph.microsoft.com/v1.0";
|
|
48
|
+
/** Bounds a busy channel roster's pagination (mirrors teams-read.ts's TEAMS_GRAPH_MAX_PAGES, a
|
|
49
|
+
* larger bound here since a roster page is denser than a message page). */
|
|
50
|
+
const TEAMS_MEMBER_MAX_PAGES = 10;
|
|
51
|
+
/** The one spelling of an unresolved answer, so no arm below can forget to name its cause. */
|
|
52
|
+
function unresolved(cause) {
|
|
53
|
+
return { member: undefined, unresolved: cause };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Is `aadObjectId` a member of Teams channel `channelId` (inside team `botTeamId`) RIGHT NOW?
|
|
57
|
+
* Resolves `botTeamId` — the activity's own Bot Connector team id — into the Microsoft 365 group id
|
|
58
|
+
* Graph takes, then paginates `GET /teams/{team}/channels/{channel}/members`, bounded at
|
|
59
|
+
* {@link TEAMS_MEMBER_MAX_PAGES}.
|
|
60
|
+
*
|
|
61
|
+
* Pure but for the injected `req` — exported (not the `req`-defaulted live wrapper below) so the
|
|
62
|
+
* resolution, the pagination and every fail-open arm are unit-testable with a canned requester, no
|
|
63
|
+
* token mint and no network call. The team-id resolution is driven in a test through
|
|
64
|
+
* teams-graph-team-id.ts's own `_seedTeamsGraphTeamIdForTest` seam rather than a second injected
|
|
65
|
+
* dependency here: one team id maps to one group id for the life of the process, so the cache IS
|
|
66
|
+
* the seam.
|
|
67
|
+
*/
|
|
68
|
+
export async function teamsRoomMembershipViaGraph(botTeamId, channelId, aadObjectId, serviceUrl, req) {
|
|
69
|
+
if (botTeamId.length === 0 || channelId.length === 0 || aadObjectId.length === 0)
|
|
70
|
+
return unresolved("missing_ids");
|
|
71
|
+
// 🔴 RESOLVE FIRST, AND NEVER FALL BACK TO `botTeamId`. A fallback would put the Connector
|
|
72
|
+
// spelling back in the Graph URL, which is the exact defect OPE-1810 removes — and because the
|
|
73
|
+
// read would then 404 into `read_error`, the fallback would be INVISIBLE in the audit trail.
|
|
74
|
+
const graphTeamId = await teamsGraphTeamId(botTeamId, serviceUrl);
|
|
75
|
+
if (graphTeamId === undefined)
|
|
76
|
+
return unresolved("team_id_unresolved");
|
|
77
|
+
try {
|
|
78
|
+
let url = `${GRAPH_BASE}/teams/${encodeURIComponent(graphTeamId)}/channels/${encodeURIComponent(channelId)}/members`;
|
|
79
|
+
for (let page = 0; page < TEAMS_MEMBER_MAX_PAGES && url; page++) {
|
|
80
|
+
const json = (await req(url));
|
|
81
|
+
// A genuine Graph success always carries `value` as an array, even an empty one — an
|
|
82
|
+
// unrecognised shape is read as UNRESOLVABLE, never as "confirmed nobody's in this channel"
|
|
83
|
+
// (the same posture routines/fire-eligibility.ts's liveRoomMembership takes on a malformed
|
|
84
|
+
// `members` field).
|
|
85
|
+
if (!Array.isArray(json.value))
|
|
86
|
+
return unresolved("malformed_response");
|
|
87
|
+
if (json.value.some((m) => m.userId === aadObjectId))
|
|
88
|
+
return { member: true };
|
|
89
|
+
url = typeof json["@odata.nextLink"] === "string" ? json["@odata.nextLink"] : undefined;
|
|
90
|
+
}
|
|
91
|
+
// `url` still set here means the page bound was hit with roster left unseen — that is NOT the
|
|
92
|
+
// same fact as "scanned every member and they weren't in it", so it stays unresolved rather than
|
|
93
|
+
// becoming a confirmed (and possibly wrong) "removed". Only a loop that ended because pagination
|
|
94
|
+
// itself ran out (`url` fell to `undefined`) has actually seen the WHOLE roster — and THAT is
|
|
95
|
+
// the answer that now skips a fire.
|
|
96
|
+
return url ? unresolved("roster_incomplete") : { member: false };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return unresolved("read_error"); // token mint failure / network error / a non-ok Graph response
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Live default: {@link teamsRoomMembershipViaGraph} through the real Graph token + transport. */
|
|
103
|
+
export const liveTeamsRoomMembership = (teamId, channelId, aadObjectId, serviceUrl) => teamsRoomMembershipViaGraph(teamId, channelId, aadObjectId, serviceUrl, liveTeamsGraphRequest);
|
|
104
|
+
/**
|
|
105
|
+
* Is `aadObjectId`'s Entra account CURRENTLY enabled? `GET /users/{id}?$select=accountEnabled`.
|
|
106
|
+
* Pure but for the injected `req` — same DI shape as {@link teamsRoomMembershipViaGraph}.
|
|
107
|
+
*/
|
|
108
|
+
export async function teamsAccountActiveViaGraph(aadObjectId, req) {
|
|
109
|
+
if (aadObjectId.length === 0)
|
|
110
|
+
return undefined;
|
|
111
|
+
try {
|
|
112
|
+
const json = (await req(`${GRAPH_BASE}/users/${encodeURIComponent(aadObjectId)}?$select=accountEnabled`));
|
|
113
|
+
// Only a POSITIVELY-TYPED boolean field counts — a response Graph returned for a different
|
|
114
|
+
// shape (or a 404/error already caught below) never reads as a fabricated "active" OR
|
|
115
|
+
// "inactive". Never a fabricated "no": the field must be `=== false`, not merely falsy/absent.
|
|
116
|
+
if (typeof json?.accountEnabled !== "boolean")
|
|
117
|
+
return undefined;
|
|
118
|
+
return json.accountEnabled;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return undefined; // 404 (unresolvable id) / auth error / transport failure ⇒ unresolvable
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Live default: {@link teamsAccountActiveViaGraph} through the real Graph token + transport. */
|
|
125
|
+
export const liveTeamsAccountActive = (aadObjectId) => teamsAccountActiveViaGraph(aadObjectId, liveTeamsGraphRequest);
|
|
126
|
+
//# sourceMappingURL=teams-fire-eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-fire-eligibility.js","sourceRoot":"","sources":["../../../agent/channels/teams-fire-eligibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { type Channel, type RouteHandlerArgs } from "eve/channels";
|
|
2
|
+
import { type TeamsMessageActivity } from "eve/channels/teams";
|
|
3
|
+
import { type OpenEverywhereTeamsAuth } from "./teams-identity.js";
|
|
4
|
+
import { type TeamsRosterProbe } from "./teams-roster.js";
|
|
5
|
+
import { type TeamsReactionActivity, type TeamsStopOutcome } from "./teams-stop.js";
|
|
6
|
+
import { type TeamsEngagementVerdict } from "./teams-engagement.js";
|
|
7
|
+
import { type TeamsSupervisorDeps } from "./teams-supervisor.js";
|
|
8
|
+
import { EventDedupe, ThreadFifo, type ExternalPolicy, type IngestedChatMessage, type AuthorTrustQuery, type SupervisorGrain, type UnifiedWindowWake, type Partition, type AttachSessionForCancel } from "@open-everywhere/core/agent/adapter-api.js";
|
|
9
|
+
/** An HTTP route handler — `RouteHandler` isn't a public eve export, so we mirror its shape
|
|
10
|
+
* (the exact slack-front mirror). */
|
|
11
|
+
type RouteHandler = (req: Request, args: RouteHandlerArgs) => Promise<Response>;
|
|
12
|
+
/** The public URL the Bot Framework POSTs every activity to (the bot registration's endpoint). */
|
|
13
|
+
export declare const PUBLIC_TEAMS_ROUTE = "/eve/v1/teams";
|
|
14
|
+
/** The internal path the wrapped `teamsChannel` is mounted at (never hit by Teams directly). */
|
|
15
|
+
export declare const INTERNAL_TEAMS_ROUTE = "/eve/v1/teams/native";
|
|
16
|
+
/** Everything the front resolved for one activity that the window OWNS — null means "forward to
|
|
17
|
+
* the native channel" (the byte-identical default). */
|
|
18
|
+
export interface TeamsFrontPlan {
|
|
19
|
+
readonly window: UnifiedWindowWake;
|
|
20
|
+
/** The DOMAIN-GRAIN surface key (`teams/<tenant>/<channel>` after the binding's segment
|
|
21
|
+
* sanitisation) — the gate/registry/FIFO/audit key. */
|
|
22
|
+
readonly surfaceKey: string;
|
|
23
|
+
readonly partition: Partition;
|
|
24
|
+
readonly grain: SupervisorGrain;
|
|
25
|
+
readonly auth: OpenEverywhereTeamsAuth;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* FOLD THE ROSTER VERDICT INTO ONE ACTIVITY'S AUTH CAPTURE (OPE-493) — the ONE step both front
|
|
29
|
+
* dispatch paths take before they stamp, and the reason the front's author leg is no longer inert.
|
|
30
|
+
*
|
|
31
|
+
* Until this existed, `resolveTeamsUserType` ran on NO live path. The front owns channel activities
|
|
32
|
+
* (the shared window) and personal/group chats (the OPE-357 DM divert), and its capture was built
|
|
33
|
+
* without a `userType` — so a measured B2B guest woke the window rendered as a `peer`
|
|
34
|
+
* (session wrun_41KZXE4FGK0GPSJB4JNM4KCMHY, 2026-08-15). The verdict now rides the SAME attribute
|
|
35
|
+
* the native path uses, so `bindTeams` → `teamsTrust` demotes the guest to `external` and the
|
|
36
|
+
* OPE-489 author leg answers, with no change of their own.
|
|
37
|
+
*
|
|
38
|
+
* THREE CALLERS, TWO LATENCY BUDGETS. The two DISPATCH paths run inside the front's
|
|
39
|
+
* `waitUntil(fifo.enqueue(…))`, so their roster GET lands AFTER the 200 ack and costs the Bot
|
|
40
|
+
* Framework nothing — they call this unconditionally. {@link liveTeamsExternalGate} runs BEFORE the
|
|
41
|
+
* ack, so it calls this only when a grain could act on the answer; its own note carries the reasoning.
|
|
42
|
+
*
|
|
43
|
+
* COST: one cached GET per (CHANNEL, user) per `identity.dmTrustTtlMs`, never one per message.
|
|
44
|
+
* `conversation.id` on a CHANNEL activity is the REPLY-CHAIN id (it carries `;messageid=<root>`), and
|
|
45
|
+
* `resolveTeamsUserType` strips that suffix for the CACHE KEY, so every reply chain in one channel
|
|
46
|
+
* shares one entry (OPE-572). The id still reaches the CONNECTOR unstripped — exactly as the native
|
|
47
|
+
* `teamsOnMessage` passes it, which is the shape the 2026-08-15 live probe measured. Whether the
|
|
48
|
+
* connector ALSO accepts a thread-qualified id is NOT established; one live GET settles it, and a
|
|
49
|
+
* refusal fails open like any other non-2xx.
|
|
50
|
+
*
|
|
51
|
+
* FAIL-OPEN, TOTALLY: a missing serviceUrl, an HTTP error, a transport throw, an unrecognised body —
|
|
52
|
+
* every one of them returns the capture UNCHANGED, by reference (see `withTeamsUserType`). The
|
|
53
|
+
* try/catch covers the probe FACTORY as well as the probe, so no roster failure can take a wake off
|
|
54
|
+
* the air.
|
|
55
|
+
*
|
|
56
|
+
* `probeFor` is the test seam: a FACTORY, not a probe, so a test can script "this activity has no
|
|
57
|
+
* probe at all" (`() => undefined`) distinctly from "the probe answers nothing". Live callers omit
|
|
58
|
+
* it and get the activity's own serviceUrl probe.
|
|
59
|
+
*/
|
|
60
|
+
export type TeamsRosterProbeFor = (activity: TeamsMessageActivity) => TeamsRosterProbe | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* The AUTHOR-TRUST question for one activity (OPE-516) — the OPERATOR's own declaration of who is
|
|
63
|
+
* internal in this tenant.
|
|
64
|
+
*
|
|
65
|
+
* 🔴 TEAMS NEEDS IT EVEN THOUGH TEAMS CAN REFUTE. The surface refutation (`teamsAuthorRefuted`) reads
|
|
66
|
+
* a resolved roster `userRole`, which every front path now resolves (OPE-493). It still does not
|
|
67
|
+
* answer everywhere: a tenant whose bot credential cannot read the roster gets no verdict, and the
|
|
68
|
+
* probe only ever REFUTES — it never declares anyone internal. The register is the leg that covers
|
|
69
|
+
* those. It is additive: an unarmed tenant declares nobody and the leg contributes nothing, so a
|
|
70
|
+
* deployment that has not armed a register sees no behaviour change at all.
|
|
71
|
+
*
|
|
72
|
+
* Returns `{}` when the tenant is unreadable — no tenant key, no arming key, no question to ask.
|
|
73
|
+
*/
|
|
74
|
+
export declare function teamsAuthorFacts(auth: unknown, authorId: string | undefined): {
|
|
75
|
+
readonly authorTrust?: AuthorTrustQuery;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The SURFACE's own vendor-external verdict for one activity: this room spans another organisation.
|
|
79
|
+
*
|
|
80
|
+
* A Teams SHARED CHANNEL (`channelData.channel.membershipType === "shared"`) is exactly that — it can
|
|
81
|
+
* hold members from tenants that are not ours, which is why teams-identity.ts already demotes one to
|
|
82
|
+
* `external` for the memory tier. Absent or any other value means "not reported", which includes "the
|
|
83
|
+
* activity did not carry channelData": absence of evidence, and this leg fails OPEN.
|
|
84
|
+
*/
|
|
85
|
+
export declare function teamsVendorExternal(a: TeamsMessageActivity): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* THE EXTERNAL-PARTICIPANT VERDICT for one Teams activity — pure, so the four outcomes are tested
|
|
88
|
+
* without a Bot-Framework call, a store or a live tenant. The four values name WHAT THE CALLER OWES;
|
|
89
|
+
* see {@link DiscordExternalVerdict}'s twin in discord-gateway.ts for the full reasoning.
|
|
90
|
+
*
|
|
91
|
+
* 🔴 THE AUTHOR GRAIN IS TESTED FIRST, AND THE ORDER MATTERS — the same ruling Discord's verdict
|
|
92
|
+
* carries. An excluded author in a silenced room is owed a notice they can act on; the room
|
|
93
|
+
* announcement tells the whole room something about the room instead.
|
|
94
|
+
*/
|
|
95
|
+
export type TeamsExternalVerdict = "proceed" | "withheld-addressed" | "withheld-ambient" | "silent-room";
|
|
96
|
+
export declare function teamsExternalVerdict(input: {
|
|
97
|
+
readonly policy: ExternalPolicy;
|
|
98
|
+
readonly author: IngestedChatMessage;
|
|
99
|
+
/** The activity @mentions the bot, or arrived in a personal chat — an ADDRESSED message is owed
|
|
100
|
+
* an answer. */
|
|
101
|
+
readonly addressed: boolean;
|
|
102
|
+
/** A personal or group chat. A DM contributes NO room leg: on a DM the trust plane asks about the
|
|
103
|
+
* PARTNER, not about the room. */
|
|
104
|
+
readonly isDm: boolean;
|
|
105
|
+
/** The SURFACE's vendor-external verdict — {@link teamsVendorExternal}. */
|
|
106
|
+
readonly vendorExternal: boolean;
|
|
107
|
+
/** The DOMAIN-grain surface key of the room, for the operator-declared leg. */
|
|
108
|
+
readonly roomKey: string;
|
|
109
|
+
/** The GROUP-rung key of the room's team, when the activity names one. */
|
|
110
|
+
readonly groupKey?: string;
|
|
111
|
+
}): TeamsExternalVerdict;
|
|
112
|
+
/**
|
|
113
|
+
* The LIVE gate: resolve the verdict for one verified message activity and pay what it owes.
|
|
114
|
+
* `true` means SUPPRESSED — the caller acks and forwards nothing.
|
|
115
|
+
*
|
|
116
|
+
* FAIL-OPEN on an unresolvable identity. If the auth capture or the partition cannot be built there
|
|
117
|
+
* is no room key, no tenant and no classification to act on, so the activity takes today's path and
|
|
118
|
+
* the existing branches apply their own fail-closed rules. Every carriage leg in this plane fails
|
|
119
|
+
* open, and this one has to as well: a config error must not silently take Teams off the air.
|
|
120
|
+
*
|
|
121
|
+
* 🔴 THE ROSTER PROBE IS PAID FOR ONLY WHEN THE POLICY CAN USE IT. This gate runs BEFORE the 200 ack,
|
|
122
|
+
* so a Bot Connector GET here is latency on EVERY activity — including the ones the front only
|
|
123
|
+
* forwards — and a slow ack makes the Bot Framework redeliver. The shipped grains (`taint` / `engage`)
|
|
124
|
+
* can suppress nothing, so `externalPolicyIsInert` skips the probe entirely and an un-opted-in
|
|
125
|
+
* deployment pays exactly what it paid before. An opted-in one pays one cached GET per
|
|
126
|
+
* (conversation, user) per TTL. This is the same trade `slack-front.ts` makes on its native
|
|
127
|
+
* `app_mention` forward, through the same predicate.
|
|
128
|
+
*/
|
|
129
|
+
export declare function liveTeamsExternalGate(a: TeamsMessageActivity, probeFor?: TeamsRosterProbeFor): Promise<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the LIVE single-window plan for one verified message activity, fail-closed: any
|
|
132
|
+
* config/partition error, the gates off, the privacy floor failing, or a missing reply target
|
|
133
|
+
* all yield null — the front then FORWARDS to the native channel, byte-identical to today.
|
|
134
|
+
*
|
|
135
|
+
* The GRAIN + VOUCH are keyed on the DOMAIN-GRAIN key (`teams/<tenant>/<channel>` — a Teams
|
|
136
|
+
* conversation id names one REPLY CHAIN, so the listable/vouchable container is the channel;
|
|
137
|
+
* memory/partition.ts's domainKeyOf resolves it via the binding's domainSurface, mirroring the
|
|
138
|
+
* Discord thread→parent-channel precedent). Operators list/vouch `teams/<tenant>` or
|
|
139
|
+
* `teams/<tenant>/<channel-id>` (sanitised segments, same as every stored key).
|
|
140
|
+
*/
|
|
141
|
+
export declare function liveTeamsFrontPlan(a: TeamsMessageActivity): TeamsFrontPlan | null;
|
|
142
|
+
/** Live supervisor deps over the (org, grain.registryKey) channel_session registry — the exact
|
|
143
|
+
* notion.ts/linear.ts shape. The wake ALWAYS diverts (teams-supervisor requires the window). */
|
|
144
|
+
/**
|
|
145
|
+
* EXPORTED FOR THE INGRESS CONTRACT TEST (OPE-1719), not for another caller. `liveTeamsDispatch` is
|
|
146
|
+
* the only production caller and it is thirty lines below. `scripts/owned-thread-ingress-wiring-test.ts`
|
|
147
|
+
* drives the `wake` member of THIS object — the real Teams ingress divert — because a test that
|
|
148
|
+
* reimplemented the wake dep would stay green after somebody moved this leg back to the raw divert,
|
|
149
|
+
* which is the exact regression the ticket closes.
|
|
150
|
+
*/
|
|
151
|
+
export declare function liveTeamsSupervisorDeps(plan: TeamsFrontPlan, to: RouteHandlerArgs["to"]): TeamsSupervisorDeps;
|
|
152
|
+
/** Injectable seams so the front is testable without live JWT verification / a Bot registration. */
|
|
153
|
+
export interface TeamsFrontOptions {
|
|
154
|
+
/** Verify one inbound request and return the raw body; throw to reject (401). Defaults to
|
|
155
|
+
* eve's `verifyTeamsRequest` with env credentials (MICROSOFT_APP_ID / TEAMS_APP_ID) — the
|
|
156
|
+
* same fallback the credential-less teamsChannel uses. */
|
|
157
|
+
readonly verify?: (req: Request) => Promise<string>;
|
|
158
|
+
readonly dedupe?: EventDedupe;
|
|
159
|
+
readonly fifo?: ThreadFifo;
|
|
160
|
+
readonly now?: () => number;
|
|
161
|
+
/** Durable first-sighting check (true = proceed). Defaults to the operational store. */
|
|
162
|
+
readonly markEventSeen?: (org: string, key: string, atIso: string) => Promise<boolean>;
|
|
163
|
+
/** The single-window plan for one verified activity. Defaults to {@link liveTeamsFrontPlan}
|
|
164
|
+
* (grain + fail-closed privacy floor); fail-closed null ⇒ forward, byte-identical. */
|
|
165
|
+
readonly planFor?: (a: TeamsMessageActivity) => TeamsFrontPlan | null;
|
|
166
|
+
/** The dispatch executor (defaults to the real supervisor dispatch over the live deps). */
|
|
167
|
+
readonly dispatch?: (a: TeamsMessageActivity, plan: TeamsFrontPlan, to: RouteHandlerArgs["to"]) => Promise<void>;
|
|
168
|
+
/** The DM window divert (OPE-357) — defaults to {@link liveTeamsDmDivert}. Injected so the front's
|
|
169
|
+
* DM-routing decision (predicate + fail-closed to native) is unit-testable without a live store. */
|
|
170
|
+
readonly dmDivert?: (a: TeamsMessageActivity, auth: OpenEverywhereTeamsAuth, partition: Partition, to: RouteHandlerArgs["to"]) => Promise<void>;
|
|
171
|
+
/** The external-participant gate (OPE-507/OPE-504) — `true` suppresses the activity. Defaults to
|
|
172
|
+
* {@link liveTeamsExternalGate}; injected so the front's ORDERING (above both branches, so the
|
|
173
|
+
* native forward is covered too) is unit-testable without a store or a Bot registration. */
|
|
174
|
+
readonly externalGate?: (a: TeamsMessageActivity) => Promise<boolean>;
|
|
175
|
+
/** The stop-reaction arm (OPE-898) — defaults to {@link routeTeamsStopReaction} over eve's cancel
|
|
176
|
+
* door. Injected so the front's ORDERING (above `parseTeamsActivity`, which never sees a reaction
|
|
177
|
+
* activity) is unit-testable without a live session. */
|
|
178
|
+
readonly stopReaction?: (rx: TeamsReactionActivity, attachSession: AttachSessionForCancel) => Promise<TeamsStopOutcome>;
|
|
179
|
+
/** The ambient engagement seam's READ (OPE-1500) — defaults to {@link resolveTeamsEngagement}.
|
|
180
|
+
* Injected so the front's ORDERING (above both the DM divert and Leg A, so one message is never
|
|
181
|
+
* handled by two legs) is unit-testable without an operational store. */
|
|
182
|
+
readonly engagementSeam?: (a: TeamsMessageActivity, machineAuthored: boolean) => Promise<TeamsEngagementVerdict>;
|
|
183
|
+
/** The ambient engagement seam's WRITE — defaults to {@link markTeamsEngaged}. Separate from the
|
|
184
|
+
* read so a test can assert that exactly one mention marks exactly one row. */
|
|
185
|
+
readonly markEngaged?: (a: TeamsMessageActivity) => Promise<string | null>;
|
|
186
|
+
/** Leg A's ROOM-grain stop READ (OPE-1511) — defaults to {@link teamsRoomWatchStopped}. Injected
|
|
187
|
+
* so the front's ORDERING (below the mention re-invitation, above the dispatch) is unit-testable
|
|
188
|
+
* without an operational store. */
|
|
189
|
+
readonly roomStopped?: (partition: Partition) => Promise<boolean>;
|
|
190
|
+
/** Leg A's ROOM-grain wake row WRITE — defaults to {@link markTeamsRoomWatched}. */
|
|
191
|
+
readonly markRoomWatched?: (partition: Partition, atIso: string) => Promise<void>;
|
|
192
|
+
/** The @mention's ROOM-grain re-invitation — defaults to {@link clearTeamsRoomStop}. Separate
|
|
193
|
+
* from the read so a test can prove a mention is the way back out of a room stop. */
|
|
194
|
+
readonly clearRoomStop?: (partition: Partition, atIso: string, engagedBy: string) => Promise<void>;
|
|
195
|
+
}
|
|
196
|
+
/** The live dispatch for one owned activity: per-message trust off the SAME auth capture the
|
|
197
|
+
* session binds with, then the teams-supervisor core over the live registry deps.
|
|
198
|
+
*
|
|
199
|
+
* `probeFor` is the roster test seam — see {@link teamsAuthWithUserType}. */
|
|
200
|
+
export declare function liveTeamsDispatch(a: TeamsMessageActivity, plan: TeamsFrontPlan, to: RouteHandlerArgs["to"], probeFor?: TeamsRosterProbeFor): Promise<void>;
|
|
201
|
+
/**
|
|
202
|
+
* THE DM WINDOW DIVERT (OPE-357) — a Teams personal/group chat lands in its OWN first-class standing
|
|
203
|
+
* window (channels/dm-seat.ts), the Teams parity of divertDiscordDmToWindow / slack-front's
|
|
204
|
+
* scheduleDmWake, and the last DM-family surface to first-class (Slack: OPE-351 stages 2+4; Discord:
|
|
205
|
+
* OPE-356).
|
|
206
|
+
*
|
|
207
|
+
* WHAT IT FIXES. Until now a Teams DM ran on eve's native per-conversation token (teams.ts's
|
|
208
|
+
* onMessage): a session with no window row, and therefore NO DISPATCHABLE ADDRESS. A DM that delegated
|
|
209
|
+
* could not be told the answer had arrived — `resolveCallerSeat` took the OPE-306 ANCHOR arm and the
|
|
210
|
+
* worker posted its own report into the DM (the OPE-358 double-post). A window token IS an address, so
|
|
211
|
+
* the DM seat now gets the whole standing-seat apparatus for free: woken, redriven, rotated (on the DM
|
|
212
|
+
* thresholds — `OE_DM_SEAT_ROTATE_*`, never the supervisor's), spend-counted and audited through the
|
|
213
|
+
* seams every other seat already uses. The worker's direct report-back stops the moment the seat
|
|
214
|
+
* becomes a real window (its caller now resolves via `ownWindowSeat`, not the anchor) — no two voices
|
|
215
|
+
* in one DM per delegated turn.
|
|
216
|
+
*
|
|
217
|
+
* SEPARATE ADMIT SPACE, NOT A FLOOR BYPASS. This never touches `teamsSingleWindowPrivacyOk`: that floor
|
|
218
|
+
* refuses `scope !== "channel"` outright because a personal/group chat may never join the SHARED
|
|
219
|
+
* cross-surface window, and it stays exactly that strict. A DM window is a STRUCTURALLY DIFFERENT,
|
|
220
|
+
* non-shared address space keyed on the DM's own surface key (`getOrCreateDmSeat`), deliverable to its
|
|
221
|
+
* own DM and nowhere else — which is precisely why it is safe to admit a DM here without weakening the
|
|
222
|
+
* channel floor (the Discord `isDm`-before-the-single-window-block precedent).
|
|
223
|
+
*
|
|
224
|
+
* 🔴 IT BYPASSES THE ENGAGEMENT GATE — a DM is always answered (see {@link buildTeamsDmWake}). The
|
|
225
|
+
* SPEND gate is NOT bypassed: `divertToUnifiedWindow` runs its own window-door gate (channels/unified.ts
|
|
226
|
+
* windowSpendGate), which is exactly the choke point every Teams single-window divert already leans on
|
|
227
|
+
* (Teams has never front-positioned a pre-gate, unlike Discord).
|
|
228
|
+
*
|
|
229
|
+
* FAIL-CLOSED: a seat-resolution or dispatch failure PROPAGATES to the FIFO's loud catch. There is
|
|
230
|
+
* deliberately NO fall-back to the native send — that would answer the human from a second session with
|
|
231
|
+
* no window row behind it, the very topology this replaces (and the OPE-351 stranding hazard).
|
|
232
|
+
*/
|
|
233
|
+
export declare function liveTeamsDmDivert(a: TeamsMessageActivity, capturedAuth: OpenEverywhereTeamsAuth, partition: Partition, to: RouteHandlerArgs["to"], probeFor?: TeamsRosterProbeFor): Promise<void>;
|
|
234
|
+
/**
|
|
235
|
+
* Build the front route handler. Verifies (fail-closed 401), classifies, then forwards or
|
|
236
|
+
* diverts. `innerHandler` is teamsChannel's own POST handler, invoked with a FRESH Request
|
|
237
|
+
* (re-readable body) + the same {@link RouteHandlerArgs}, so teamsChannel re-verifies and
|
|
238
|
+
* dispatches exactly as if it owned the URL — byte-identical whenever no plan resolves.
|
|
239
|
+
*/
|
|
240
|
+
export declare function buildTeamsFrontHandler(innerHandler: RouteHandler, opts?: TeamsFrontOptions): RouteHandler;
|
|
241
|
+
/**
|
|
242
|
+
* Compose the front route with a wrapped `teamsChannel`. `inner` must have been built with
|
|
243
|
+
* `route: INTERNAL_TEAMS_ROUTE`. Returns the SAME channel value (preserving eve's internal
|
|
244
|
+
* adapter/receive) with the public front route prepended — so the Bot Framework's URL hits us,
|
|
245
|
+
* and both routes share one channel name → one session namespace (the frontSlackChannel shape).
|
|
246
|
+
*/
|
|
247
|
+
export declare function frontTeamsChannel<C extends Channel<any, any, any>>(inner: C, opts?: TeamsFrontOptions): C;
|
|
248
|
+
export {};
|
|
249
|
+
//# sourceMappingURL=teams-front.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-front.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-front.ts"],"names":[],"mappings":"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"}
|