@open-everywhere/provider-teams 0.4.0 → 0.5.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 (32) hide show
  1. package/agent/approvals/teams-card.ts +188 -0
  2. package/agent/approvals/teams-flow.ts +467 -0
  3. package/agent/channels/teams-declared-target.ts +108 -0
  4. package/agent/channels/teams-front.ts +25 -2
  5. package/agent/docs-generated.ts +1 -1
  6. package/agent/register.ts +28 -1
  7. package/agent/surface-parity.ts +19 -11
  8. package/dist/agent/approvals/teams-card.d.ts +65 -0
  9. package/dist/agent/approvals/teams-card.d.ts.map +1 -0
  10. package/dist/agent/approvals/teams-card.js +134 -0
  11. package/dist/agent/approvals/teams-card.js.map +1 -0
  12. package/dist/agent/approvals/teams-flow.d.ts +144 -0
  13. package/dist/agent/approvals/teams-flow.d.ts.map +1 -0
  14. package/dist/agent/approvals/teams-flow.js +366 -0
  15. package/dist/agent/approvals/teams-flow.js.map +1 -0
  16. package/dist/agent/channels/teams-declared-target.d.ts +12 -0
  17. package/dist/agent/channels/teams-declared-target.d.ts.map +1 -0
  18. package/dist/agent/channels/teams-declared-target.js +96 -0
  19. package/dist/agent/channels/teams-declared-target.js.map +1 -0
  20. package/dist/agent/channels/teams-front.d.ts +5 -0
  21. package/dist/agent/channels/teams-front.d.ts.map +1 -1
  22. package/dist/agent/channels/teams-front.js +19 -2
  23. package/dist/agent/channels/teams-front.js.map +1 -1
  24. package/dist/agent/docs-generated.js +1 -1
  25. package/dist/agent/docs-generated.js.map +1 -1
  26. package/dist/agent/register.d.ts.map +1 -1
  27. package/dist/agent/register.js +26 -2
  28. package/dist/agent/register.js.map +1 -1
  29. package/dist/agent/surface-parity.d.ts.map +1 -1
  30. package/dist/agent/surface-parity.js +18 -11
  31. package/dist/agent/surface-parity.js.map +1 -1
  32. package/package.json +15 -2
@@ -0,0 +1,65 @@
1
+ import type { PendingActionRow } from "@open-everywhere/core/agent/adapter-api.js";
2
+ /** The `invoke` activity name Teams gives an `Action.Execute` tap. */
3
+ export declare const TEAMS_ADAPTIVE_CARD_ACTION_INVOKE = "adaptiveCard/action";
4
+ /** The `verb` every OE approval action carries. The decision rides `data`, so one verb covers all. */
5
+ export declare const OE_TEAMS_APPROVAL_VERB = "oe-approval";
6
+ /** The `data` key every OE approval action carries. Distinct from eve's HITL key (`eve_input`). */
7
+ export declare const OE_TEAMS_APPROVAL_DATA_KEY = "oe_teams_approval";
8
+ /** The Adaptive Card attachment content type. */
9
+ export declare const TEAMS_ADAPTIVE_CARD_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
10
+ /**
11
+ * Every verb a Teams approval action can carry — the same closed set the Discord bundle uses, and a
12
+ * SUBSET of core's `ApprovalDecision`. `contradict` is the judge-lift notice's one button: it decides
13
+ * nothing, it records an overrule of a verdict whose act has already run.
14
+ */
15
+ export type TeamsApprovalDecision = "approve" | "reject" | "approve_session" | "contradict";
16
+ /** One decoded OE approval action payload. */
17
+ export interface TeamsApprovalAction {
18
+ readonly id: string;
19
+ readonly decision: TeamsApprovalDecision;
20
+ readonly org: string;
21
+ }
22
+ /** One Adaptive Card element or action. Loosely typed on purpose, as card.ts's SlackBlock is. */
23
+ export interface AdaptiveElement {
24
+ readonly type: string;
25
+ readonly [key: string]: unknown;
26
+ }
27
+ /** One rendered Adaptive Card. */
28
+ export interface TeamsAdaptiveCard {
29
+ readonly type: "AdaptiveCard";
30
+ readonly $schema: string;
31
+ readonly version: string;
32
+ readonly body: readonly AdaptiveElement[];
33
+ readonly actions?: readonly AdaptiveElement[];
34
+ }
35
+ /** The `data` object of one OE approval action. */
36
+ export declare function buildTeamsApprovalData(row: Pick<PendingActionRow, "id" | "org">, decision: TeamsApprovalDecision): Record<string, unknown>;
37
+ /**
38
+ * Decode an OE approval payload from an action's `data` object. Returns null for anything else —
39
+ * a foreign data object, a missing id or org, or a decision outside the closed set — so a caller
40
+ * reads "not parseable" as "not ours" with no second check (core's parseApprovalAction does the same).
41
+ */
42
+ export declare function parseTeamsApprovalData(data: unknown): TeamsApprovalAction | null;
43
+ /**
44
+ * The card for a still-pending action: the plain sentence, the summary, "requested by / expires", and one `Action.Execute` per layout option. Mirrors card.ts's pendingCardBlocks. The requester is the Bot Framework principal id, because an Adaptive Card mention needs the person's display name, and the frozen row carries only the id.
45
+ * The OPTIONS come from core's card layout (OPE-53, OPE-2363: only a ROUTINE's card gets the middle grant option), so Slack, Discord and Teams cannot disagree, and OPE-2456's extra option lands here with no change.
46
+ */
47
+ export declare function pendingTeamsCard(row: PendingActionRow, summaryLines: readonly string[]): TeamsAdaptiveCard;
48
+ /**
49
+ * The card once a decision has landed: the same sentence and summary, no actions, one outcome line. It decides
50
+ * nothing — it renders what the caller already CAS-decided. `deciderLabel` is the name the click
51
+ * activity carried, when the caller has one; the row's principal id otherwise.
52
+ */
53
+ export declare function decidedTeamsCard(row: PendingActionRow, summaryLines: readonly string[], outcomeLine: string, deciderLabel?: string): TeamsAdaptiveCard;
54
+ /**
55
+ * The judge-lift notice (OPE-1103 on Discord, OPE-2424 here): the trace of an act the armed judge
56
+ * lifted, with the one "This should not have run" button. NOT an approval card — no Approve, no
57
+ * Reject, no expiry line — because the act has already run.
58
+ */
59
+ export declare function judgeLiftNoticeTeamsCard(row: PendingActionRow, summaryLines: readonly string[], outcomeLine: string): TeamsAdaptiveCard;
60
+ /** One Adaptive Card as a message-activity attachment. */
61
+ export declare function teamsCardAttachment(c: TeamsAdaptiveCard): {
62
+ readonly contentType: string;
63
+ readonly content: TeamsAdaptiveCard;
64
+ };
65
+ //# sourceMappingURL=teams-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-card.d.ts","sourceRoot":"","sources":["../../../agent/approvals/teams-card.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAkB,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAGnG,sEAAsE;AACtE,eAAO,MAAM,iCAAiC,wBAAwB,CAAC;AAEvE,sGAAsG;AACtG,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD,mGAAmG;AACnG,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAE9D,iDAAiD;AACjD,eAAO,MAAM,gCAAgC,4CAA4C,CAAC;AAK1F;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,QAAQ,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAK5F,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,iGAAiG;AACjG,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,kCAAkC;AAClC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAED,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,KAAK,CAAC,EAAE,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE1I;AAUD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAUhF;AA+CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CAI1G;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAStJ;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,iBAAiB,CASvI;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,iBAAiB,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAE/H"}
@@ -0,0 +1,134 @@
1
+ // Pure Teams renderers + the action-data codec for the approval card (OPE-2424) — the Teams sibling
2
+ // of core's card.ts (Slack Block Kit) and the Discord bundle's discord-card.ts. PURE by the same
3
+ // doctrine: no I/O, nothing async. The card is a CODE-RENDERED VIEW of the FROZEN args, never model
4
+ // text. The I/O half (the Connector poster and the invoke handler) lives in teams-flow.ts.
5
+ //
6
+ // Teams differs from Slack and Discord in three ways, and all three are handled here:
7
+ // - The card is an ADAPTIVE CARD attachment on a message activity. The body is TextBlocks, and the
8
+ // buttons are `Action.Execute` actions.
9
+ // - `Action.Execute` (Universal Actions), not `Action.Submit`. A tap arrives as an `invoke` activity
10
+ // named `adaptiveCard/action`, and the HTTP response to that invoke can carry a REPLACEMENT card
11
+ // (the decided render) or a message only the clicker sees (a refusal). That is the same response
12
+ // shape Discord's interaction reply has, so the click path maps one resolution to one response.
13
+ // - The payload rides the action's `data` object, not an id string. The org rides there too, for
14
+ // the reason Slack puts it in the button `value`: the click handler needs the org BEFORE it can
15
+ // look the row up. The data key is distinct from eve's own HITL key (`eve_input`), so the front
16
+ // route can tell an OE approval tap from an eve HITL answer without a second check.
17
+ import { approvalLabel, pendingCardLayout, plainSummaryForRow } from "@open-everywhere/core/agent/adapter-api.js";
18
+ /** The `invoke` activity name Teams gives an `Action.Execute` tap. */
19
+ export const TEAMS_ADAPTIVE_CARD_ACTION_INVOKE = "adaptiveCard/action";
20
+ /** The `verb` every OE approval action carries. The decision rides `data`, so one verb covers all. */
21
+ export const OE_TEAMS_APPROVAL_VERB = "oe-approval";
22
+ /** The `data` key every OE approval action carries. Distinct from eve's HITL key (`eve_input`). */
23
+ export const OE_TEAMS_APPROVAL_DATA_KEY = "oe_teams_approval";
24
+ /** The Adaptive Card attachment content type. */
25
+ export const TEAMS_ADAPTIVE_CARD_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
26
+ /** Universal Actions (`Action.Execute`) need schema 1.4 or later. */
27
+ const ADAPTIVE_CARD_VERSION = "1.4";
28
+ /** The closed verb set as a VALUE, so build and parse cannot drift apart. */
29
+ const TEAMS_APPROVAL_DECISIONS = ["approve", "reject", "approve_session", "contradict"];
30
+ /** The `data` object of one OE approval action. */
31
+ export function buildTeamsApprovalData(row, decision) {
32
+ return { [OE_TEAMS_APPROVAL_DATA_KEY]: { id: row.id, decision, org: row.org } };
33
+ }
34
+ function isObject(v) {
35
+ return typeof v === "object" && v !== null && !Array.isArray(v);
36
+ }
37
+ function nonEmpty(v) {
38
+ return typeof v === "string" && v.length > 0 ? v : undefined;
39
+ }
40
+ /**
41
+ * Decode an OE approval payload from an action's `data` object. Returns null for anything else —
42
+ * a foreign data object, a missing id or org, or a decision outside the closed set — so a caller
43
+ * reads "not parseable" as "not ours" with no second check (core's parseApprovalAction does the same).
44
+ */
45
+ export function parseTeamsApprovalData(data) {
46
+ if (!isObject(data))
47
+ return null;
48
+ const payload = data[OE_TEAMS_APPROVAL_DATA_KEY];
49
+ if (!isObject(payload))
50
+ return null;
51
+ const id = nonEmpty(payload["id"]);
52
+ const org = nonEmpty(payload["org"]);
53
+ const decision = payload["decision"];
54
+ if (!id || !org || typeof decision !== "string")
55
+ return null;
56
+ if (!TEAMS_APPROVAL_DECISIONS.includes(decision))
57
+ return null;
58
+ return { id, decision: decision, org };
59
+ }
60
+ function textBlock(text, extra = {}) {
61
+ return { type: "TextBlock", text, wrap: true, ...extra };
62
+ }
63
+ function card(body, actions) {
64
+ return {
65
+ type: "AdaptiveCard",
66
+ $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
67
+ version: ADAPTIVE_CARD_VERSION,
68
+ body,
69
+ ...(actions && actions.length > 0 ? { actions } : {}),
70
+ };
71
+ }
72
+ function execute(row, title, decision, style) {
73
+ return {
74
+ type: "Action.Execute",
75
+ title,
76
+ verb: OE_TEAMS_APPROVAL_VERB,
77
+ data: buildTeamsApprovalData(row, decision),
78
+ ...(style ? { style } : {}),
79
+ };
80
+ }
81
+ /** The shared card body: header, then one TextBlock per summary line, then the provenance line. */
82
+ function cardBody(header, summaryLines, footer) {
83
+ return [
84
+ textBlock(header, { weight: "Bolder", size: "Medium" }),
85
+ ...summaryLines.map((line) => textBlock(line)),
86
+ textBlock(footer, { isSubtle: true, size: "Small" }),
87
+ ];
88
+ }
89
+ /** An approval card's body: the PLAIN SENTENCE first (OPE-2434), then the `Approval requested: <kind>` label, the summary lines and the provenance line. The sentence went through core's `plainValue`, so it carries no markdown a TextBlock would render. */
90
+ function approvalCardBody(row, summaryLines, footer) {
91
+ return [
92
+ textBlock(plainSummaryForRow(row), { weight: "Bolder", size: "Medium" }),
93
+ textBlock(approvalLabel(row.kind), { isSubtle: true, size: "Small", spacing: "None" }),
94
+ ...summaryLines.map((line) => textBlock(line)),
95
+ textBlock(footer, { isSubtle: true, size: "Small" }),
96
+ ];
97
+ }
98
+ const OPTION_STYLE = { primary: "positive", danger: "destructive", neutral: undefined };
99
+ /**
100
+ * The card for a still-pending action: the plain sentence, the summary, "requested by / expires", and one `Action.Execute` per layout option. Mirrors card.ts's pendingCardBlocks. The requester is the Bot Framework principal id, because an Adaptive Card mention needs the person's display name, and the frozen row carries only the id.
101
+ * The OPTIONS come from core's card layout (OPE-53, OPE-2363: only a ROUTINE's card gets the middle grant option), so Slack, Discord and Teams cannot disagree, and OPE-2456's extra option lands here with no change.
102
+ */
103
+ export function pendingTeamsCard(row, summaryLines) {
104
+ const layout = pendingCardLayout(row, summaryLines);
105
+ const actions = layout.options.map((o) => execute(row, o.label, o.verb, OPTION_STYLE[o.tone]));
106
+ return card(approvalCardBody(row, summaryLines, `Requested by ${layout.requestedBy} · ${layout.expiry}`), actions);
107
+ }
108
+ /**
109
+ * The card once a decision has landed: the same sentence and summary, no actions, one outcome line. It decides
110
+ * nothing — it renders what the caller already CAS-decided. `deciderLabel` is the name the click
111
+ * activity carried, when the caller has one; the row's principal id otherwise.
112
+ */
113
+ export function decidedTeamsCard(row, summaryLines, outcomeLine, deciderLabel) {
114
+ const who = deciderLabel ?? row.decidedBy ?? "unknown";
115
+ const decided = row.status === "approved"
116
+ ? `✅ Approved by ${who} · ${outcomeLine}`
117
+ : row.status === "rejected"
118
+ ? `❌ Rejected by ${who}${outcomeLine === "rejected" ? "" : ` · ${outcomeLine}`}`
119
+ : "⏰ Expired";
120
+ return card(approvalCardBody(row, summaryLines, decided));
121
+ }
122
+ /**
123
+ * The judge-lift notice (OPE-1103 on Discord, OPE-2424 here): the trace of an act the armed judge
124
+ * lifted, with the one "This should not have run" button. NOT an approval card — no Approve, no
125
+ * Reject, no expiry line — because the act has already run.
126
+ */
127
+ export function judgeLiftNoticeTeamsCard(row, summaryLines, outcomeLine) {
128
+ return card(cardBody(`Ran without asking: ${row.kind}`, summaryLines, `⚡ Auto-approved by the approval judge — no card was posted and this has already run · ${outcomeLine}`), [execute(row, "This should not have run", "contradict", "destructive")]);
129
+ }
130
+ /** One Adaptive Card as a message-activity attachment. */
131
+ export function teamsCardAttachment(c) {
132
+ return { contentType: TEAMS_ADAPTIVE_CARD_CONTENT_TYPE, content: c };
133
+ }
134
+ //# sourceMappingURL=teams-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-card.js","sourceRoot":"","sources":["../../../agent/approvals/teams-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ import { resolveApprovalClick, type ApprovalClickResolution, type DecisionFeedbackSurface, type DecisionWakeDeps, type PendingActionRow, type UnifiedWakeOrigin } from "@open-everywhere/core/agent/adapter-api.js";
2
+ import { type TeamsApprovalAction } from "./teams-card.js";
3
+ import { type TeamsConversationRef } from "../channels/teams-conversation-ref.js";
4
+ /** The Connector send this file uses, injectable so a test drives it without Teams. */
5
+ export type TeamsSend = (input: {
6
+ readonly serviceUrl: string;
7
+ readonly conversationId: string;
8
+ readonly body: unknown;
9
+ }) => Promise<{
10
+ id: string;
11
+ }>;
12
+ /** The Connector update this file uses, injectable for the same reason. */
13
+ export type TeamsUpdate = (input: {
14
+ readonly serviceUrl: string;
15
+ readonly conversationId: string;
16
+ readonly activityId: string;
17
+ readonly body: unknown;
18
+ }) => Promise<{
19
+ id: string;
20
+ }>;
21
+ /** Injectable seams for the poster and the row-addressed notice. Live callers pass none. */
22
+ export interface TeamsPostDeps {
23
+ readonly send?: TeamsSend;
24
+ /** The stored conversation reference for a card's `channelId`. Live default: the raw-id restore
25
+ * (`teams-id-restore.ts`) and then the `conversation`-grain address (`teams-conversation-ref.ts`). */
26
+ readonly resolveRef?: (channelId: string) => Promise<TeamsConversationRef | undefined>;
27
+ }
28
+ /**
29
+ * Render the approval card and send it into `row.channelId` through the Bot Connector. Resolves the
30
+ * posted activity id, or undefined on ANY failure: no channel id, no stored conversation reference
31
+ * (this deployment never saw an inbound activity there), a missing Bot credential, a Connector error.
32
+ * NEVER throws.
33
+ *
34
+ * WHERE IT LANDS. `row.channelId` is the Teams CHANNEL id (bindTeams' `target.channelId`), so the card
35
+ * posts as a NEW post in the channel, not inside the reply chain that asked. bindTeams sets no
36
+ * `threadTs` on purpose (about twenty consumers read that field as a Slack thread_ts), so the row
37
+ * carries no chain to thread under. The tool result names the card, and the decision notice threads
38
+ * under the card itself.
39
+ */
40
+ export declare function postTeamsApprovalCard(row: PendingActionRow, summaryLines: readonly string[], deps?: TeamsPostDeps): Promise<string | undefined>;
41
+ /**
42
+ * THE JUDGE-LIFT NOTICE POSTER: the room's trace of an act the armed judge lifted, with the overrule
43
+ * button. Best-effort by contract — the frozen args have already run, so a failed post is logged and
44
+ * resolves void. The audit rows (`approval.autoapproved`, `approval.judge.verdict`) land either way.
45
+ */
46
+ export declare function postTeamsJudgeLiftNotice(row: PendingActionRow, summaryLines: readonly string[], outcomeLine: string, deps?: TeamsPostDeps): Promise<void>;
47
+ /**
48
+ * The decision notice for a row with NO click in hand — a TTL expiry or a session-grant release. It
49
+ * posts into the card's own conversation at the grain the card was sent to. It does not try to thread
50
+ * under the card: a reply-chain address needs a vendor id shape this file would have to guess, and the
51
+ * click path below threads exactly, off the invoke's own conversation id. Throws on failure; core's
52
+ * `deliverDecisionFeedback` swallows a notice failure.
53
+ */
54
+ export declare function postTeamsRowDecisionNotice(row: PendingActionRow, text: string, deps?: TeamsPostDeps): Promise<unknown>;
55
+ /** The decision-wake target for a row with no click in hand. Null when the card's conversation has no
56
+ * stored reference or the row has no `postedTo`. The authority frozen at proposal still wins over it. */
57
+ export declare function teamsRowWakeTarget(row: PendingActionRow, deps?: TeamsPostDeps): Promise<{
58
+ surfaceKey: string;
59
+ origin: UnifiedWakeOrigin;
60
+ } | null>;
61
+ /** The legs this bundle registers with core (`registerDecisionFeedbackSurface`). */
62
+ export declare const teamsDecisionFeedback: DecisionFeedbackSurface;
63
+ /** What one OE approval tap carries. Every field but `action` comes off the relayed Teams activity
64
+ * (`channelId === "msteams"`, see the header), never out of the card's data payload. The JWT proves
65
+ * the relay, not the body. */
66
+ export interface TeamsApprovalInvoke {
67
+ readonly action: TeamsApprovalAction;
68
+ /** The Bot Framework user id of the person who tapped (`from.id`) — the principal id bindTeams uses. */
69
+ readonly clickerId?: string;
70
+ /** The display name Teams sent with the tap, for the decided card only. Never an authority input. */
71
+ readonly clickerName?: string;
72
+ readonly tenantId?: string;
73
+ /** The Teams channel the tap happened in (`channelData.channel.id`), when it was a channel. */
74
+ readonly teamsChannelId?: string;
75
+ /** The conversation the card lives in, at the grain Teams reports it (a channel card's own reply chain). */
76
+ readonly conversationId: string;
77
+ readonly serviceUrl: string;
78
+ /** The card's own activity id, when Teams carries it on the invoke. */
79
+ readonly replyToId?: string;
80
+ }
81
+ /**
82
+ * Probe one parsed activity for an OE approval tap: an `invoke` named `adaptiveCard/action` whose
83
+ * `value.action.data` carries the OE payload. Returns null for anything else — an eve HITL answer, a
84
+ * different invoke, a message — which the front forwards to the native channel untouched. NEVER throws.
85
+ */
86
+ export declare function parseTeamsApprovalInvoke(activity: unknown): TeamsApprovalInvoke | null;
87
+ /**
88
+ * The room the tap PHYSICALLY happened in, as `teams/<tenant>/<channel>` — core's clicker gate reads
89
+ * it (leg C compares its channel segment with the row's `channelId`). It comes from the relayed
90
+ * Teams activity: the channel id, else the channel grain of the conversation id. Undefined without a
91
+ * tenant or a room. 🔴 The handler REFUSES such a tap rather than passing no click context: core
92
+ * reserves "no click context" for the headless control plane, and it switches every clicker-gate leg
93
+ * off.
94
+ *
95
+ * TWO SPELLINGS OF ONE ROOM. A card the venue walk posted in its own room carries the RAW channel id;
96
+ * a card redirected to a domain's roster entry carries the roster's SURFACE-KEY spelling. Both
97
+ * spellings below are derived from the TAP's own channel id, so the row only chooses which spelling
98
+ * of the same click room to compare. `teams-id-restore.ts` states the bound: two raw ids that share a
99
+ * spelling are already one id to this deployment.
100
+ */
101
+ export declare function teamsClickSurfaceKey(invoke: TeamsApprovalInvoke, row: Pick<PendingActionRow, "channelId"> | null): string | undefined;
102
+ /** One `adaptiveCard/action` invoke response body. */
103
+ export interface TeamsInvokeResponseBody {
104
+ readonly statusCode: 200;
105
+ readonly type: string;
106
+ readonly value: unknown;
107
+ }
108
+ /**
109
+ * Map one decide resolution to the invoke response — the Teams rendering of what core's Slack handler
110
+ * renders as calls. PURE, so it is tested as a matrix.
111
+ * decided / expired → the decided card REPLACES the pending one (no buttons left to tap).
112
+ * denied, clicker-denied, halted → a note to the clicker alone; the card stays live and the row
113
+ * stays pending, so a person who may decide it still can.
114
+ * contradicted → a note to the clicker; the notice card stays, because it is the room's trace.
115
+ * unknown → a note, with the Slack card-text wording.
116
+ * the silent branches (a stale re-tap, a lost race, an unregistered kind) → a short note that
117
+ * nothing changed. An invoke must answer with something, and re-rendering a card
118
+ * from a stale row could overwrite the winner's decided card.
119
+ */
120
+ export declare function teamsResponseFor(res: ApprovalClickResolution, deciderLabel?: string): TeamsInvokeResponseBody;
121
+ /** Test seams for the tap. Live callers pass only `waitUntil`. */
122
+ export interface TeamsApprovalDeps {
123
+ /** The decide core. Live default: `resolveApprovalClick`. */
124
+ readonly resolve?: typeof resolveApprovalClick;
125
+ /** Leg D's positive per-clicker verdict. Live default: the Bot Connector member probe the inbound
126
+ * message path and the HITL answer path already use. `undefined` leaves leg D inert. */
127
+ readonly userType?: (invoke: TeamsApprovalInvoke) => Promise<"guest" | "member" | undefined>;
128
+ /** Reads the row ONLY to pick the spelling of the click room (see {@link teamsClickSurfaceKey}). */
129
+ readonly readRow?: (org: string, id: string) => Promise<PendingActionRow | null>;
130
+ readonly send?: TeamsSend;
131
+ readonly update?: TeamsUpdate;
132
+ readonly decisionWake?: DecisionWakeDeps;
133
+ /** The route's own `waitUntil`. PRESENT ⇒ the feedback and core's session-grant release run off
134
+ * the response path. ABSENT ⇒ both are awaited first, which is correct and slower. */
135
+ readonly waitUntil?: (p: Promise<unknown>) => void;
136
+ }
137
+ /**
138
+ * Handle one VERIFIED OE approval tap and return the invoke response body. The approver policy is
139
+ * core's: the config-as-code roster, matched as `teams/<from.id>` (Teams is not a tenant-scoped
140
+ * person-id space, identity/principal-key.ts), with no platform-native admin leg — a Teams team owner
141
+ * is not an OE authority. This call states its platform rather than letting core infer it. NEVER throws.
142
+ */
143
+ export declare function handleTeamsApprovalInvoke(invoke: TeamsApprovalInvoke, deps?: TeamsApprovalDeps): Promise<TeamsInvokeResponseBody>;
144
+ //# sourceMappingURL=teams-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-flow.d.ts","sourceRoot":"","sources":["../../../agent/approvals/teams-flow.ts"],"names":[],"mappings":"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"}