@dbx-tools/teams 0.3.44 → 0.4.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/lib/index.d.ts +28 -0
- package/lib/index.js +24 -0
- package/lib/src/auth.d.ts +94 -0
- package/lib/src/auth.js +232 -0
- package/lib/src/builder.d.ts +29 -0
- package/lib/src/builder.js +96 -0
- package/lib/src/config.d.ts +148 -0
- package/lib/src/config.js +163 -0
- package/lib/src/connector.d.ts +53 -0
- package/lib/src/connector.js +82 -0
- package/lib/src/conversation.d.ts +234 -0
- package/lib/src/conversation.js +509 -0
- package/lib/src/defaults.d.ts +59 -0
- package/lib/src/defaults.js +61 -0
- package/lib/src/messaging.d.ts +74 -0
- package/lib/src/messaging.js +114 -0
- package/lib/src/plugin.d.ts +185 -0
- package/lib/src/plugin.js +430 -0
- package/lib/src/runtime.d.ts +63 -0
- package/lib/src/runtime.js +143 -0
- package/lib/src/tool.d.ts +67 -0
- package/lib/src/tool.js +71 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -7
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Teams conversation turn: run a Mastra agent against an inbound Bot
|
|
3
|
+
* Framework activity and answer with Adaptive Card attachments.
|
|
4
|
+
*
|
|
5
|
+
* This is the piece that makes `POST /api/teams/activity` behave like a Teams
|
|
6
|
+
* bot rather than a chat API that happens to return JSON. A turn is:
|
|
7
|
+
*
|
|
8
|
+
* 1. read the user's text off the inbound `message` activity;
|
|
9
|
+
* 2. ANSWER it - a normal tool-using agent turn, with no mention of cards, so
|
|
10
|
+
* the agent queries Genie / calls its tools exactly as it would on a
|
|
11
|
+
* streaming chat endpoint;
|
|
12
|
+
* 3. FORMAT that answer into a {@link card.CardSpec} in a second pass, via
|
|
13
|
+
* Mastra's `structuredOutput` (prompt-injected, see
|
|
14
|
+
* {@link JSON_PROMPT_INJECTION});
|
|
15
|
+
* 4. compile the spec with the same deterministic builder the
|
|
16
|
+
* `create_teams_card` tool uses, and attach it to an outbound activity.
|
|
17
|
+
*
|
|
18
|
+
* The two passes are the important part. Asking for the answer AND the card
|
|
19
|
+
* shape in ONE request makes the model treat formatting as the task: it emits a
|
|
20
|
+
* card straight away and never calls its tools, so a question that should have
|
|
21
|
+
* queried a data source came back as "I don't have a real system connected -
|
|
22
|
+
* here is a template card with placeholders". Answering first, then formatting a
|
|
23
|
+
* REAL answer, makes this endpoint's content identical to the streaming
|
|
24
|
+
* endpoint's; only the presentation differs.
|
|
25
|
+
*
|
|
26
|
+
* Formatting is also why the turn does not simply rely on the agent calling
|
|
27
|
+
* `create_teams_card`: on this endpoint a card IS the response format, so it
|
|
28
|
+
* should be a property of the turn rather than a tool the model may forget.
|
|
29
|
+
* Agents keep the tool for the other direction - answering in prose on a normal
|
|
30
|
+
* chat endpoint and choosing to attach a card. When the agent DOES call it
|
|
31
|
+
* during the answering pass, that spec wins and the formatting pass is skipped.
|
|
32
|
+
*
|
|
33
|
+
* The conversation id doubles as the agent's memory thread id, so a client that
|
|
34
|
+
* keeps posting the same `conversation.id` gets a continuous conversation - the
|
|
35
|
+
* same mapping a real channel relies on.
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
38
|
+
*/
|
|
39
|
+
import { activity as activityContract, card } from "@dbx-tools/shared-teams";
|
|
40
|
+
/**
|
|
41
|
+
* Minimal structural shape of the Mastra `Agent` this module drives.
|
|
42
|
+
*
|
|
43
|
+
* Declared structurally rather than importing `@mastra/core`'s `Agent`: this
|
|
44
|
+
* package must not depend on the Mastra plugin (the plugin depends on nothing
|
|
45
|
+
* here either), and a turn only ever needs `generate`. Any object with a
|
|
46
|
+
* compatible `generate` satisfies it, which also makes the turn trivially
|
|
47
|
+
* testable with a stub.
|
|
48
|
+
*/
|
|
49
|
+
export interface CardAgentLike {
|
|
50
|
+
generate(prompt: string, options: {
|
|
51
|
+
structuredOutput?: {
|
|
52
|
+
schema: typeof card.cardSpecSchema;
|
|
53
|
+
jsonPromptInjection?: boolean | "system" | "inline";
|
|
54
|
+
};
|
|
55
|
+
memory?: {
|
|
56
|
+
thread: string;
|
|
57
|
+
resource: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Mastra's per-turn `RequestContext`. Opaque here - the object comes from
|
|
61
|
+
* the agent plugin (see {@link AgentProviderLike.exports}) and is only
|
|
62
|
+
* forwarded - but REQUIRED for parity with the chat endpoints: Mastra's
|
|
63
|
+
* user-scoped tools read the AppKit user off it, so a turn without one
|
|
64
|
+
* answers "the data source is unreachable" where chat answers with data.
|
|
65
|
+
*/
|
|
66
|
+
requestContext?: unknown;
|
|
67
|
+
abortSignal?: AbortSignal;
|
|
68
|
+
}): Promise<AgentResult>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The slice of Mastra's `generate` result a turn reads.
|
|
72
|
+
*
|
|
73
|
+
* `toolResults` matters as much as `object` here: when structured output fails
|
|
74
|
+
* and the turn re-asks as prose, an agent holding the `create_teams_card` tool
|
|
75
|
+
* typically CALLS it, so the best available card spec is the tool's arguments -
|
|
76
|
+
* already in the right vocabulary - rather than anything in the prose.
|
|
77
|
+
*/
|
|
78
|
+
export interface AgentResult {
|
|
79
|
+
object?: unknown;
|
|
80
|
+
text?: string;
|
|
81
|
+
toolResults?: {
|
|
82
|
+
payload?: {
|
|
83
|
+
toolName?: string;
|
|
84
|
+
args?: unknown;
|
|
85
|
+
};
|
|
86
|
+
toolName?: string;
|
|
87
|
+
args?: unknown;
|
|
88
|
+
}[];
|
|
89
|
+
}
|
|
90
|
+
/** The bot's identity on outbound activities when the caller names none. */
|
|
91
|
+
export declare const BOT_ACCOUNT: activityContract.ChannelAccount;
|
|
92
|
+
/**
|
|
93
|
+
* Instructions for the FORMATTING pass only - turning an answer the agent has
|
|
94
|
+
* already produced into the card vocabulary.
|
|
95
|
+
*
|
|
96
|
+
* Deliberately NOT sent with the user's question. Asking for a card and an
|
|
97
|
+
* answer in one request makes the model treat formatting as the task: it emits a
|
|
98
|
+
* card immediately instead of calling its tools, so a question that should have
|
|
99
|
+
* queried Genie came back as "I don't have a real system connected - here is a
|
|
100
|
+
* template card". Formatting is a separate, second pass over a real answer.
|
|
101
|
+
*/
|
|
102
|
+
export declare const CARD_FORMAT_INSTRUCTIONS: string;
|
|
103
|
+
/**
|
|
104
|
+
* Nudge added to the ANSWERING pass.
|
|
105
|
+
*
|
|
106
|
+
* The agent answers the question normally here - tools included - so this only
|
|
107
|
+
* asks for the shape that survives compression into a card well. It must not
|
|
108
|
+
* mention cards, or the model starts formatting instead of answering.
|
|
109
|
+
*/
|
|
110
|
+
export declare const CARD_ANSWER_INSTRUCTIONS: string;
|
|
111
|
+
/**
|
|
112
|
+
* Structural shape of the sibling plugin that owns the agent registry - the
|
|
113
|
+
* slice of `@dbx-tools/appkit-mastra`'s `exports()` a turn needs.
|
|
114
|
+
*
|
|
115
|
+
* Matched structurally, by registered plugin NAME, rather than importing the
|
|
116
|
+
* Mastra plugin: this package stays a leaf add-on (it depends on no other
|
|
117
|
+
* dbx-tools runtime package, like node-email), the dependency direction stays
|
|
118
|
+
* one-way, and any plugin exposing the same `get` / `getDefault` pair can back
|
|
119
|
+
* the endpoint.
|
|
120
|
+
*/
|
|
121
|
+
export interface AgentProviderLike {
|
|
122
|
+
exports(): {
|
|
123
|
+
get(id: string): unknown;
|
|
124
|
+
getDefault(): unknown;
|
|
125
|
+
/**
|
|
126
|
+
* Builds the per-turn `RequestContext` the provider's tools expect. Optional
|
|
127
|
+
* so an older provider (or a differently-shaped one) still resolves an
|
|
128
|
+
* agent; the turn simply runs without user-scoped tool context then.
|
|
129
|
+
*/
|
|
130
|
+
createRequestContext?(options: {
|
|
131
|
+
threadId?: string;
|
|
132
|
+
resourceId?: string;
|
|
133
|
+
}): Promise<unknown>;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve the agent that answers a turn from the AppKit plugin registry.
|
|
138
|
+
*
|
|
139
|
+
* `agentId` picks a specific agent; omitted, the provider's default agent
|
|
140
|
+
* answers. Returns `null` when the provider is absent or the id is unknown, so
|
|
141
|
+
* the route can answer 503 / 404 instead of throwing.
|
|
142
|
+
*/
|
|
143
|
+
export declare const resolveCardAgent: (plugins: ReadonlyMap<string, unknown> | undefined, providerName: string, agentId?: string) => CardAgentLike | null;
|
|
144
|
+
/** Options for {@link runCardTurn}. */
|
|
145
|
+
export interface CardTurnOptions {
|
|
146
|
+
/** Cancels the agent call with the request. */
|
|
147
|
+
signal?: AbortSignal;
|
|
148
|
+
/** Overrides the bot identity stamped on the reply. */
|
|
149
|
+
bot?: activityContract.ChannelAccount;
|
|
150
|
+
/**
|
|
151
|
+
* Builds the Mastra `RequestContext` for the turn, from
|
|
152
|
+
* {@link resolveCardContextFactory}. Omitted, the turn still answers, but
|
|
153
|
+
* without the AppKit user its user-scoped tools cannot reach Databricks - so
|
|
154
|
+
* the route should always supply it.
|
|
155
|
+
*/
|
|
156
|
+
createRequestContext?: CardContextFactory;
|
|
157
|
+
}
|
|
158
|
+
/** Builds the per-turn request context the agent's tools read. */
|
|
159
|
+
export type CardContextFactory = (options: {
|
|
160
|
+
threadId?: string;
|
|
161
|
+
resourceId?: string;
|
|
162
|
+
}) => Promise<unknown>;
|
|
163
|
+
/**
|
|
164
|
+
* The agent plugin's request-context factory, when it exposes one.
|
|
165
|
+
*
|
|
166
|
+
* Resolved separately from the agent because it is the piece that gives an
|
|
167
|
+
* out-of-band turn the same tool reach as a chat turn; a provider without it
|
|
168
|
+
* still answers, just without user-scoped tools.
|
|
169
|
+
*/
|
|
170
|
+
export declare const resolveCardContextFactory: (plugins: ReadonlyMap<string, unknown> | undefined, providerName: string) => CardContextFactory | null;
|
|
171
|
+
/**
|
|
172
|
+
* The text a `message` activity carries, or `null` when it carries none.
|
|
173
|
+
*
|
|
174
|
+
* A channel sends plenty of activities with no usable text (a `typing`
|
|
175
|
+
* indicator, a `conversationUpdate` when someone joins, or a `message` whose
|
|
176
|
+
* payload is only an attachment). Those are not errors - they simply produce no
|
|
177
|
+
* reply - so this returns `null` rather than throwing.
|
|
178
|
+
*/
|
|
179
|
+
export declare const promptOf: (inbound: activityContract.Activity) => string | null;
|
|
180
|
+
/**
|
|
181
|
+
* Build an outbound activity carrying `cards`, addressed back to the sender of
|
|
182
|
+
* `inbound`.
|
|
183
|
+
*
|
|
184
|
+
* Exported because a client rendering an optimistic local reply, and a test
|
|
185
|
+
* asserting the envelope, both need the same construction the turn uses.
|
|
186
|
+
*/
|
|
187
|
+
export declare const toReplyActivity: (inbound: activityContract.Activity, cards: card.AdaptiveCard[], options?: {
|
|
188
|
+
bot?: activityContract.ChannelAccount;
|
|
189
|
+
text?: string;
|
|
190
|
+
}) => activityContract.Activity;
|
|
191
|
+
/**
|
|
192
|
+
* Recover a {@link card.CardSpec} from a full Adaptive Card DOCUMENT.
|
|
193
|
+
*
|
|
194
|
+
* A capable model asked for "a Microsoft Teams Adaptive Card" often answers with
|
|
195
|
+
* the finished 1.5 document instead of the small spec - correct Adaptive Card
|
|
196
|
+
* JSON, wrong schema, so `structuredOutput` rejects it (`title: expected
|
|
197
|
+
* string`) and a genuinely good answer is thrown away. Reading the document back
|
|
198
|
+
* into the spec vocabulary keeps it: the heading TextBlocks become
|
|
199
|
+
* title/subtitle, remaining TextBlocks the body, the FactSet the facts, and any
|
|
200
|
+
* `Action.OpenUrl` the actions.
|
|
201
|
+
*
|
|
202
|
+
* Deliberately tolerant about the container: `type` may be missing and the body
|
|
203
|
+
* may nest elements inside a `Container` / `ColumnSet`, so blocks are collected
|
|
204
|
+
* recursively and anything unrecognized is ignored.
|
|
205
|
+
*/
|
|
206
|
+
export declare const documentCardSpec: (value: unknown) => card.CardSpec | null;
|
|
207
|
+
/**
|
|
208
|
+
* The card the model produced, wherever it ended up.
|
|
209
|
+
*
|
|
210
|
+
* `structuredOutput` rejects anything off-schema by THROWING, and Mastra puts
|
|
211
|
+
* the offending payload on the error (`details.value`) - so the model's real
|
|
212
|
+
* answer is recoverable from a failed call. Both the spec shape and a full
|
|
213
|
+
* Adaptive Card document are accepted, from an object or from JSON text.
|
|
214
|
+
*/
|
|
215
|
+
export declare const rejectedCardSpec: (err: unknown) => card.CardSpec | null;
|
|
216
|
+
/**
|
|
217
|
+
* Run one conversation turn: drive `agent` with the inbound activity's text and
|
|
218
|
+
* return the activities to append to the transcript.
|
|
219
|
+
*
|
|
220
|
+
* Returns an EMPTY array for an activity that carries no prompt (a typing
|
|
221
|
+
* indicator, a join event), which is exactly what a bot does with one - the
|
|
222
|
+
* route answers `{ activities: [] }` and the transcript is unchanged.
|
|
223
|
+
*
|
|
224
|
+
* Two agent calls, in this order:
|
|
225
|
+
*
|
|
226
|
+
* 1. the ANSWER - no `structuredOutput`, so the agent's tools are available
|
|
227
|
+
* and the content matches what the streaming endpoint would say;
|
|
228
|
+
* 2. the FORMAT - {@link formatAsCard} compiling that answer into a card.
|
|
229
|
+
*
|
|
230
|
+
* Every failure mode still yields the answer: a card the agent composed with
|
|
231
|
+
* `create_teams_card` during pass 1 short-circuits pass 2, and a failed pass 2
|
|
232
|
+
* falls back to {@link toCardSpec} over the answer text.
|
|
233
|
+
*/
|
|
234
|
+
export declare const runCardTurn: (agent: CardAgentLike, inbound: activityContract.Activity, options?: CardTurnOptions) => Promise<activityContract.Activity[]>;
|