@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.
Files changed (146) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +46 -2
  4. package/agent/channels/teams-bot-name.ts +162 -0
  5. package/agent/channels/teams-catch-up.ts +244 -0
  6. package/agent/channels/teams-connector.ts +183 -0
  7. package/agent/channels/teams-conversation-ref.ts +398 -0
  8. package/agent/channels/teams-directory.ts +193 -0
  9. package/agent/channels/teams-engagement.ts +391 -0
  10. package/agent/channels/teams-fire-eligibility.ts +152 -0
  11. package/agent/channels/teams-front.ts +1404 -0
  12. package/agent/channels/teams-graph-boundary.ts +178 -0
  13. package/agent/channels/teams-graph-team-id.ts +106 -0
  14. package/agent/channels/teams-id-restore.ts +131 -0
  15. package/agent/channels/teams-identity.ts +395 -0
  16. package/agent/channels/teams-input-response.ts +129 -0
  17. package/agent/channels/teams-message-ops.ts +79 -0
  18. package/agent/channels/teams-origin-codec.ts +104 -0
  19. package/agent/channels/teams-post-arm.ts +323 -0
  20. package/agent/channels/teams-reaction-feedback.ts +127 -0
  21. package/agent/channels/teams-read.ts +510 -0
  22. package/agent/channels/teams-roster.ts +136 -0
  23. package/agent/channels/teams-stop.ts +319 -0
  24. package/agent/channels/teams-supervisor.ts +638 -0
  25. package/agent/channels/teams.ts +270 -0
  26. package/agent/contributions.ts +31 -0
  27. package/agent/docs-generated.ts +15 -0
  28. package/agent/ops.ts +129 -0
  29. package/agent/provider-events.ts +69 -0
  30. package/agent/register.ts +219 -0
  31. package/agent/surface-parity.ts +112 -0
  32. package/dist/agent/channels/teams-bot-name.d.ts +36 -0
  33. package/dist/agent/channels/teams-bot-name.d.ts.map +1 -0
  34. package/dist/agent/channels/teams-bot-name.js +141 -0
  35. package/dist/agent/channels/teams-bot-name.js.map +1 -0
  36. package/dist/agent/channels/teams-catch-up.d.ts +53 -0
  37. package/dist/agent/channels/teams-catch-up.d.ts.map +1 -0
  38. package/dist/agent/channels/teams-catch-up.js +221 -0
  39. package/dist/agent/channels/teams-catch-up.js.map +1 -0
  40. package/dist/agent/channels/teams-connector.d.ts +57 -0
  41. package/dist/agent/channels/teams-connector.d.ts.map +1 -0
  42. package/dist/agent/channels/teams-connector.js +132 -0
  43. package/dist/agent/channels/teams-connector.js.map +1 -0
  44. package/dist/agent/channels/teams-conversation-ref.d.ts +123 -0
  45. package/dist/agent/channels/teams-conversation-ref.d.ts.map +1 -0
  46. package/dist/agent/channels/teams-conversation-ref.js +368 -0
  47. package/dist/agent/channels/teams-conversation-ref.js.map +1 -0
  48. package/dist/agent/channels/teams-directory.d.ts +11 -0
  49. package/dist/agent/channels/teams-directory.d.ts.map +1 -0
  50. package/dist/agent/channels/teams-directory.js +179 -0
  51. package/dist/agent/channels/teams-directory.js.map +1 -0
  52. package/dist/agent/channels/teams-engagement.d.ts +174 -0
  53. package/dist/agent/channels/teams-engagement.d.ts.map +1 -0
  54. package/dist/agent/channels/teams-engagement.js +336 -0
  55. package/dist/agent/channels/teams-engagement.js.map +1 -0
  56. package/dist/agent/channels/teams-fire-eligibility.d.ts +26 -0
  57. package/dist/agent/channels/teams-fire-eligibility.d.ts.map +1 -0
  58. package/dist/agent/channels/teams-fire-eligibility.js +126 -0
  59. package/dist/agent/channels/teams-fire-eligibility.js.map +1 -0
  60. package/dist/agent/channels/teams-front.d.ts +249 -0
  61. package/dist/agent/channels/teams-front.d.ts.map +1 -0
  62. package/dist/agent/channels/teams-front.js +1143 -0
  63. package/dist/agent/channels/teams-front.js.map +1 -0
  64. package/dist/agent/channels/teams-graph-boundary.d.ts +45 -0
  65. package/dist/agent/channels/teams-graph-boundary.d.ts.map +1 -0
  66. package/dist/agent/channels/teams-graph-boundary.js +168 -0
  67. package/dist/agent/channels/teams-graph-boundary.js.map +1 -0
  68. package/dist/agent/channels/teams-graph-team-id.d.ts +30 -0
  69. package/dist/agent/channels/teams-graph-team-id.d.ts.map +1 -0
  70. package/dist/agent/channels/teams-graph-team-id.js +89 -0
  71. package/dist/agent/channels/teams-graph-team-id.js.map +1 -0
  72. package/dist/agent/channels/teams-id-restore.d.ts +34 -0
  73. package/dist/agent/channels/teams-id-restore.d.ts.map +1 -0
  74. package/dist/agent/channels/teams-id-restore.js +123 -0
  75. package/dist/agent/channels/teams-id-restore.js.map +1 -0
  76. package/dist/agent/channels/teams-identity.d.ts +148 -0
  77. package/dist/agent/channels/teams-identity.d.ts.map +1 -0
  78. package/dist/agent/channels/teams-identity.js +336 -0
  79. package/dist/agent/channels/teams-identity.js.map +1 -0
  80. package/dist/agent/channels/teams-input-response.d.ts +23 -0
  81. package/dist/agent/channels/teams-input-response.d.ts.map +1 -0
  82. package/dist/agent/channels/teams-input-response.js +102 -0
  83. package/dist/agent/channels/teams-input-response.js.map +1 -0
  84. package/dist/agent/channels/teams-message-ops.d.ts +3 -0
  85. package/dist/agent/channels/teams-message-ops.d.ts.map +1 -0
  86. package/dist/agent/channels/teams-message-ops.js +72 -0
  87. package/dist/agent/channels/teams-message-ops.js.map +1 -0
  88. package/dist/agent/channels/teams-origin-codec.d.ts +16 -0
  89. package/dist/agent/channels/teams-origin-codec.d.ts.map +1 -0
  90. package/dist/agent/channels/teams-origin-codec.js +99 -0
  91. package/dist/agent/channels/teams-origin-codec.js.map +1 -0
  92. package/dist/agent/channels/teams-post-arm.d.ts +37 -0
  93. package/dist/agent/channels/teams-post-arm.d.ts.map +1 -0
  94. package/dist/agent/channels/teams-post-arm.js +280 -0
  95. package/dist/agent/channels/teams-post-arm.js.map +1 -0
  96. package/dist/agent/channels/teams-reaction-feedback.d.ts +22 -0
  97. package/dist/agent/channels/teams-reaction-feedback.d.ts.map +1 -0
  98. package/dist/agent/channels/teams-reaction-feedback.js +108 -0
  99. package/dist/agent/channels/teams-reaction-feedback.js.map +1 -0
  100. package/dist/agent/channels/teams-read.d.ts +127 -0
  101. package/dist/agent/channels/teams-read.d.ts.map +1 -0
  102. package/dist/agent/channels/teams-read.js +424 -0
  103. package/dist/agent/channels/teams-read.js.map +1 -0
  104. package/dist/agent/channels/teams-roster.d.ts +54 -0
  105. package/dist/agent/channels/teams-roster.d.ts.map +1 -0
  106. package/dist/agent/channels/teams-roster.js +121 -0
  107. package/dist/agent/channels/teams-roster.js.map +1 -0
  108. package/dist/agent/channels/teams-stop.d.ts +74 -0
  109. package/dist/agent/channels/teams-stop.d.ts.map +1 -0
  110. package/dist/agent/channels/teams-stop.js +252 -0
  111. package/dist/agent/channels/teams-stop.js.map +1 -0
  112. package/dist/agent/channels/teams-supervisor.d.ts +351 -0
  113. package/dist/agent/channels/teams-supervisor.d.ts.map +1 -0
  114. package/dist/agent/channels/teams-supervisor.js +366 -0
  115. package/dist/agent/channels/teams-supervisor.js.map +1 -0
  116. package/dist/agent/channels/teams.d.ts +5 -0
  117. package/dist/agent/channels/teams.d.ts.map +1 -0
  118. package/dist/agent/channels/teams.js +246 -0
  119. package/dist/agent/channels/teams.js.map +1 -0
  120. package/dist/agent/contributions.d.ts +12 -0
  121. package/dist/agent/contributions.d.ts.map +1 -0
  122. package/dist/agent/contributions.js +31 -0
  123. package/dist/agent/contributions.js.map +1 -0
  124. package/dist/agent/docs-generated.d.ts +3 -0
  125. package/dist/agent/docs-generated.d.ts.map +1 -0
  126. package/dist/agent/docs-generated.js +13 -0
  127. package/dist/agent/docs-generated.js.map +1 -0
  128. package/dist/agent/ops.d.ts +14 -0
  129. package/dist/agent/ops.d.ts.map +1 -0
  130. package/dist/agent/ops.js +122 -0
  131. package/dist/agent/ops.js.map +1 -0
  132. package/dist/agent/provider-events.d.ts +3 -0
  133. package/dist/agent/provider-events.d.ts.map +1 -0
  134. package/dist/agent/provider-events.js +67 -0
  135. package/dist/agent/provider-events.js.map +1 -0
  136. package/dist/agent/register.d.ts +16 -0
  137. package/dist/agent/register.d.ts.map +1 -0
  138. package/dist/agent/register.js +198 -0
  139. package/dist/agent/register.js.map +1 -0
  140. package/dist/agent/surface-parity.d.ts +3 -0
  141. package/dist/agent/surface-parity.d.ts.map +1 -0
  142. package/dist/agent/surface-parity.js +106 -0
  143. package/dist/agent/surface-parity.js.map +1 -0
  144. package/package.json +183 -4
  145. package/tsconfig.build.json +22 -0
  146. 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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"}