@open-everywhere/provider-teams 0.0.0-stage → 0.1.1
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,108 @@
|
|
|
1
|
+
// THE TEAMS REACTION-FEEDBACK ARM (OPE-1502) — a reaction on the agent's own message becomes a
|
|
2
|
+
// durable feedback signal, the same way it already does on Slack and Discord.
|
|
3
|
+
//
|
|
4
|
+
// The inbound half is FREE on this surface: Teams already delivers `messageReaction` activities, and
|
|
5
|
+
// the stop arm (channels/teams-stop.ts) already parses them. What was missing is the second consumer
|
|
6
|
+
// — the one that turns a 👍 or a 👎 on the agent's own reply into a row a person can query. This
|
|
7
|
+
// module is that consumer, and it adds NO second parse and NO second ingress: it takes the activity
|
|
8
|
+
// the stop arm already parsed and hands it core's surface-neutral `ingestReaction`.
|
|
9
|
+
//
|
|
10
|
+
// ── WHAT MICROSOFT DELIVERS, AND WHY IT SHAPES THE GUARDS
|
|
11
|
+
//
|
|
12
|
+
// 1. 🔴 TEAMS SENDS THE EVENT ONLY FOR MESSAGES THE AGENT ITSELF SENT ("a user adds or removes
|
|
13
|
+
// reactions to a message, which was sent by your agent" — learn.microsoft.com, "Conversation
|
|
14
|
+
// events"). That FENCE is what makes the feedback case the common case here rather than the rare
|
|
15
|
+
// one: a reaction on somebody else's message never reaches this arm at all.
|
|
16
|
+
// 2. 🔴 `replyToId` NAMES THAT MESSAGE, AND ITS PRESENCE IS THE WHOLE `onBotReply` TEST — WHICH IS
|
|
17
|
+
// NOT THE CHECK THE OTHER SURFACES MAKE. Slack, Discord and Linear each compare the reacted
|
|
18
|
+
// message's AUTHOR to the bot's own id. Teams delivers no author for the reacted message, so no
|
|
19
|
+
// such comparison exists here: the guarantee is Microsoft's fence (point 1), and the code checks
|
|
20
|
+
// only that a target id is present. An activity carrying `replyToId` for a message the agent did
|
|
21
|
+
// not write would be recorded as feedback, and nothing in this repository could notice. The
|
|
22
|
+
// absence of `replyToId` reads as "no message to name" — telemetry only, never a feedback row.
|
|
23
|
+
// 3. THE REACTION VOCABULARY IS SIX FIXED VALUES: `angry`, `heart`, `laugh`, `like`, `sad`,
|
|
24
|
+
// `surprised`. Core's `classifyEmoji` buckets them; `surprised` is `other`, which is correct —
|
|
25
|
+
// surprise is not a verdict.
|
|
26
|
+
// 4. The activity carries no message content, so nothing here reads or needs any.
|
|
27
|
+
//
|
|
28
|
+
// ── WHY THE STOP REACTION NEVER REACHES HERE
|
|
29
|
+
//
|
|
30
|
+
// `angry` is this surface's stop trigger (channels/teams-stop.ts). ✋ is a MECHANISM, not feedback,
|
|
31
|
+
// and the same rule holds for the glyph that stands in for it: an ADDED `angry` means "stop this" and
|
|
32
|
+
// must never ALSO be recorded as a sentiment about the message it sits on. The stop arm therefore
|
|
33
|
+
// keeps that activity and this arm never sees it. A REMOVED `angry` is not a stop — nothing cancels
|
|
34
|
+
// on a removal — so it arrives here as an ordinary retraction, exactly as a removed ✋ does on Slack.
|
|
35
|
+
import { buildIngestedReaction, ingestReaction, liveReactionSink, resolvePartition, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
36
|
+
import { buildTeamsAuth } from "./teams-identity.js";
|
|
37
|
+
/** Production deps: the live audit/telemetry sink core already owns. There is no Teams-specific
|
|
38
|
+
* sink — the whole point of the seam is that the durable row is written in ONE place. */
|
|
39
|
+
export function liveTeamsReactionFeedbackDeps() {
|
|
40
|
+
return { sink: liveReactionSink() };
|
|
41
|
+
}
|
|
42
|
+
/** The Bot Framework's own prefix for an application id. A reactor wearing it is a bot whatever the
|
|
43
|
+
* `recipient` field says — the same belt-and-braces check the stop arm makes. */
|
|
44
|
+
function teamsReactorIsMachine(rx) {
|
|
45
|
+
return rx.reactorId === rx.agentId || rx.reactorId?.startsWith("28:") === true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ingest every reaction one Teams `messageReaction` activity carries. Returns one outcome per
|
|
49
|
+
* reaction — added first, then removed — so a caller's log says what happened to each.
|
|
50
|
+
*
|
|
51
|
+
* It NEVER THROWS into the caller: core's sinks are best-effort by contract, and a lost feedback row
|
|
52
|
+
* must never cost the ingress its ack.
|
|
53
|
+
*
|
|
54
|
+
* An activity with nothing to ingest returns an EMPTY array. That is the honest answer for a bot's
|
|
55
|
+
* own reaction, an activity with no reactor, and an activity naming no conversation — none of the
|
|
56
|
+
* three is a reaction from a person on a message this deployment can key.
|
|
57
|
+
*/
|
|
58
|
+
export async function routeTeamsReactionFeedback(rx, deps) {
|
|
59
|
+
// The agent's own reactions ride the same activity stream and are never feedback. 🔴 THE `28:`
|
|
60
|
+
// PREFIX IS THE LOAD-BEARING HALF of this guard, not the `recipient` comparison: with an
|
|
61
|
+
// unresolved agent id the prefix is the only thing left, and — unlike the ingest paths on Slack and
|
|
62
|
+
// Discord, where a missed bot reactor costs a stray telemetry row — a missed one HERE costs a
|
|
63
|
+
// durable feedback row attributed to a machine, because `onBotReply` rests on a present
|
|
64
|
+
// `replyToId` rather than on an author comparison (header, point 2).
|
|
65
|
+
if (!rx.reactorId || teamsReactorIsMachine(rx))
|
|
66
|
+
return [];
|
|
67
|
+
if (!rx.conversationId)
|
|
68
|
+
return [];
|
|
69
|
+
const partition = resolvePartition({
|
|
70
|
+
channel: { kind: "teams" },
|
|
71
|
+
session: {
|
|
72
|
+
auth: {
|
|
73
|
+
current: buildTeamsAuth({
|
|
74
|
+
userId: rx.reactorId,
|
|
75
|
+
conversationId: rx.conversationId,
|
|
76
|
+
...(rx.tenantId ? { tenantId: rx.tenantId } : {}),
|
|
77
|
+
...(rx.teamsChannelId ? { channelId: rx.teamsChannelId } : {}),
|
|
78
|
+
...(rx.teamId ? { teamId: rx.teamId } : {}),
|
|
79
|
+
...(rx.scope ? { scope: rx.scope } : {}),
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
// A PRESENCE CHECK, not an author comparison — the header's point 2 says why this surface offers
|
|
85
|
+
// no other, and what that costs. No `replyToId` means no message to name, so the reaction is
|
|
86
|
+
// telemetry only.
|
|
87
|
+
const onBotReply = Boolean(rx.replyToId);
|
|
88
|
+
const outcomes = [];
|
|
89
|
+
for (const [verb, emojis] of [
|
|
90
|
+
["added", rx.added],
|
|
91
|
+
["removed", rx.removed],
|
|
92
|
+
]) {
|
|
93
|
+
for (const emoji of emojis) {
|
|
94
|
+
const reaction = buildIngestedReaction({
|
|
95
|
+
surface: "teams",
|
|
96
|
+
verb,
|
|
97
|
+
reactorId: rx.reactorId,
|
|
98
|
+
emoji,
|
|
99
|
+
target: { channelId: rx.conversationId, messageId: rx.replyToId ?? "" },
|
|
100
|
+
onBotReply,
|
|
101
|
+
partition,
|
|
102
|
+
});
|
|
103
|
+
outcomes.push(await ingestReaction(reaction, deps.sink));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return outcomes;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=teams-reaction-feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-reaction-feedback.js","sourceRoot":"","sources":["../../../agent/channels/teams-reaction-feedback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { type BindableContext, type Domain, type ReadScopeAuditDeps, type WakeReadAddress } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
/** Test-only reset — drops the held Graph token AND the boundary determination it produced (the
|
|
3
|
+
* two are minted together; leaving the boundary behind would let one test's token decide the next
|
|
4
|
+
* test's audit marker). */
|
|
5
|
+
export declare function _resetTeamsGraphTokenCache(): void;
|
|
6
|
+
/** A Graph GET, given a full (absolute) URL — mints its own token, returns the parsed JSON body.
|
|
7
|
+
* Fails LOUD on any non-ok response (matching discordReadAdapter's posture, not the fail-soft
|
|
8
|
+
* backscroll fetchers). Injectable so tests never mint a token or hit the network. */
|
|
9
|
+
export type TeamsGraphRequester = (url: string) => Promise<unknown>;
|
|
10
|
+
/** Exported (OPE-522) so channels/teams-fire-eligibility.ts's Graph probes reuse the SAME token
|
|
11
|
+
* mint + GET plumbing rather than standing up a second one — the routine fire-eligibility leg's
|
|
12
|
+
* requests are ordinary Graph reads, no different in shape from a channel-history read. Callers
|
|
13
|
+
* that need "unresolvable" rather than a thrown error (fire-eligibility's fail-open posture) catch
|
|
14
|
+
* around it themselves — this function's own fail-LOUD contract is unchanged. */
|
|
15
|
+
export declare const liveTeamsGraphRequest: TeamsGraphRequester;
|
|
16
|
+
interface GraphChatMessage {
|
|
17
|
+
readonly id?: string;
|
|
18
|
+
readonly createdDateTime?: string;
|
|
19
|
+
readonly deletedDateTime?: string | null;
|
|
20
|
+
readonly messageType?: string;
|
|
21
|
+
readonly from?: {
|
|
22
|
+
readonly user?: {
|
|
23
|
+
readonly id?: string;
|
|
24
|
+
readonly displayName?: string;
|
|
25
|
+
};
|
|
26
|
+
readonly application?: {
|
|
27
|
+
readonly id?: string;
|
|
28
|
+
readonly displayName?: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
readonly body?: {
|
|
32
|
+
readonly contentType?: string;
|
|
33
|
+
readonly content?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** One Teams message rendered to a compact, model-friendly line — structurally identical to
|
|
37
|
+
* slack-read.ts's RenderedMessage / channel-read.ts's RenderedLine (no cross-import: same
|
|
38
|
+
* no-shared-type convention slack-read.ts itself uses). */
|
|
39
|
+
export interface TeamsRenderedLine {
|
|
40
|
+
readonly author: string;
|
|
41
|
+
readonly ts: string;
|
|
42
|
+
readonly text: string;
|
|
43
|
+
/** The author's Entra (AAD) object id, for the OPE-474 non-member probe. Absent on an
|
|
44
|
+
* application-authored or unattributable line. Never reaches the model — the read verbs project
|
|
45
|
+
* author/ts/text only. It is a DIFFERENT value from `author`, which prefers the display NAME. */
|
|
46
|
+
readonly authorId?: string;
|
|
47
|
+
/** An APPLICATION author (`from.application`, no `from.user`). Excluded from the probe: a machine
|
|
48
|
+
* is below a member but is not a person outside the org. */
|
|
49
|
+
readonly bot?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** HTML message body → plain text: drop tags (mention `<at>…</at>` spans included), decode the
|
|
52
|
+
* five common entities, collapse whitespace. Pure — a self-contained copy of the Teams e2e
|
|
53
|
+
* driver's stripHtml (scripts/e2e/teams-driver.ts); see the file header for why it's not shared. */
|
|
54
|
+
export declare function stripHtml(html: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Normalize a raw Graph messages array (either endpoint's `value`) into oldest-first
|
|
57
|
+
* {@link TeamsRenderedLine}[] — dropping deleted/system messages and empty bodies, HTML-stripped.
|
|
58
|
+
* Graph guarantees neither endpoint's order (no `$orderby` support, and both are observed
|
|
59
|
+
* newest-first in practice — see the file header), so this ALWAYS sorts by `createdDateTime`
|
|
60
|
+
* rather than trusting API order. Pure; exported for the render test (mirrors discordLinesFromRaw).
|
|
61
|
+
*/
|
|
62
|
+
export declare function teamsLinesFromRaw(raw: unknown): TeamsRenderedLine[];
|
|
63
|
+
/**
|
|
64
|
+
* The same normalization, with each line's CREATE TIME kept (epoch ms). The read verbs do not need it
|
|
65
|
+
* — a rendered line carries the message id in `ts`, not a timestamp — but the seat catch-up does: it
|
|
66
|
+
* bounds its window by time and core orders the whole window by it (channels/seat-catch-up.ts).
|
|
67
|
+
* Exported so `teams-catch-up.ts` shares this one normalization rather than re-deriving the sender
|
|
68
|
+
* kind and the system-message filter, which is the whole way those two could disagree about a
|
|
69
|
+
* message. `createdMs` is 0 when Graph sent no readable `createdDateTime`; the caller drops it.
|
|
70
|
+
*/
|
|
71
|
+
export declare function teamsTimedLinesFromRaw(raw: unknown): Array<TeamsRenderedLine & {
|
|
72
|
+
readonly createdMs: number;
|
|
73
|
+
}>;
|
|
74
|
+
/** Follow `@odata.nextLink` until `limit` messages are in hand or {@link TEAMS_GRAPH_MAX_PAGES}
|
|
75
|
+
* pages are exhausted — a hard bound so a busy channel/thread can't fan out unboundedly (mirrors
|
|
76
|
+
* discord-backscroll.ts's DISCORD_RAW_FETCH_MAX_PAGES). Returns the raw messages, unsorted
|
|
77
|
+
* (teamsLinesFromRaw sorts). Propagates any requester error (fail-loud). */
|
|
78
|
+
export declare function fetchGraphPages(req: TeamsGraphRequester, initialUrl: string, limit: number): Promise<GraphChatMessage[]>;
|
|
79
|
+
export declare function channelMessagesUrl(teamId: string, channelId: string): string;
|
|
80
|
+
export declare function threadRepliesUrl(teamId: string, channelId: string, rootMessageId: string): string;
|
|
81
|
+
/** Mirrors binding.ts's private `seg()` sanitizer exactly (same regex, same trim, same 100-char
|
|
82
|
+
* cap) so a raw Teams channel id compares correctly against the already-sanitized domain grain.
|
|
83
|
+
* Pure; exported for the allowlist test. */
|
|
84
|
+
export declare function sanitizeTeamsChannelId(value: string): string;
|
|
85
|
+
/** The Teams CHANNEL ids this session may read (SANITIZED — see the header note): the current
|
|
86
|
+
* channel (its channel grain) plus every Teams channel this session's explicit domain lists. Pure
|
|
87
|
+
* but for the injected `domains` — mirrors channel-read.ts's allowedSurfaceIdsFrom shape so it is
|
|
88
|
+
* unit-testable the same way. */
|
|
89
|
+
export declare function allowedTeamsChannelIds(channelGrainKey: string, domains: readonly Domain[]): {
|
|
90
|
+
ids: Set<string>;
|
|
91
|
+
domain?: string;
|
|
92
|
+
};
|
|
93
|
+
/** Assert a requested (raw) Teams channel id is readable by this session — else AUDIT the refusal
|
|
94
|
+
* and throw (fail closed), the same ChannelScopeError shape slack-read/channel-read raise.
|
|
95
|
+
* Sanitizes `channelId` before comparing (see the header note) — the RAW value is what the Graph
|
|
96
|
+
* call itself uses, and the SANITIZED one is what the row records, so a row compares like-for-like
|
|
97
|
+
* against the domain's `reach`. The decision is unchanged; only the row is new. */
|
|
98
|
+
export declare function assertTeamsChannelAllowed(ctx: BindableContext, channelId: string, deps?: ReadScopeAuditDeps): Promise<void>;
|
|
99
|
+
export declare const teamsReadAdapter: {
|
|
100
|
+
platform: "teams";
|
|
101
|
+
/**
|
|
102
|
+
* OPE-929 — Teams' own spelling of "which room and which thread is this turn in", and the one of
|
|
103
|
+
* the three that no core-side guess could have got right.
|
|
104
|
+
*
|
|
105
|
+
* 🔴 THE BINDING'S CONVERSATION IS NOT THE READ CONTAINER. A Teams CHANNEL activity binds the
|
|
106
|
+
* REPLY CHAIN as its `surface` / `anchorChannelId` (the conversation id carries
|
|
107
|
+
* `;messageid=<root>`), while both Graph read URLs below take the CHANNEL id
|
|
108
|
+
* (`/teams/{team}/channels/{channel}/messages[/{root}/replies]`). So the container is
|
|
109
|
+
* `target.channelId` and the thread root is the `;messageid=` half of the conversation id — the
|
|
110
|
+
* two halves of one value, pulled apart here rather than anywhere else.
|
|
111
|
+
*
|
|
112
|
+
* A personal or group chat carries no channel id and no reply-chain suffix, so it reports no
|
|
113
|
+
* address at all: Graph's chat-message read is delegated-only, and this adapter reads channels.
|
|
114
|
+
* The read then refuses loudly, which is the honest answer for a surface it cannot serve.
|
|
115
|
+
*/
|
|
116
|
+
wakeAddress(ctx: BindableContext): WakeReadAddress | undefined;
|
|
117
|
+
readChannel(ctx: BindableContext, channel: string, limit?: number, req?: TeamsGraphRequester): Promise<readonly TeamsRenderedLine[]>;
|
|
118
|
+
readThread(ctx: BindableContext, channel: string, threadTs: string, limit?: number, req?: TeamsGraphRequester): Promise<readonly TeamsRenderedLine[]>;
|
|
119
|
+
/** OPE-474 / OPE-534 — the author-trust register only; see the note above for why the Entra probe
|
|
120
|
+
* is not reachable from a read. No `memberships` leg: an armed Teams tenant matches on principal
|
|
121
|
+
* keys (`teams/<entraObjectId>`), which is what a Teams declaration lists. It answers with the
|
|
122
|
+
* refuted AUTHOR IDS, because core reads the one answer twice: the turn mark and the `withhold`
|
|
123
|
+
* body replacement. */
|
|
124
|
+
refutedAuthorIds(ctx: BindableContext, lines: readonly TeamsRenderedLine[]): Promise<readonly string[]>;
|
|
125
|
+
};
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=teams-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-read.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-read.ts"],"names":[],"mappings":"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"}
|