@novu/framework 2.11.1 → 2.11.2-alpha.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.
Files changed (89) hide show
  1. package/README.md +114 -9
  2. package/ai-sdk/package.json +4 -0
  3. package/cards/package.json +4 -0
  4. package/dist/{esm/index-D6L0Yufw.d.ts → cjs/agent.types-Dqm2VBTO.d.cts} +410 -193
  5. package/dist/cjs/ai-sdk/index.cjs +1 -0
  6. package/dist/cjs/ai-sdk/index.d.cts +40 -0
  7. package/dist/cjs/cards.cjs +57 -0
  8. package/dist/cjs/cards.d.cts +2 -0
  9. package/dist/cjs/{health-check.types-D4NEyjyC.d.cts → health-check.types-B9FbzRbm.d.cts} +6 -140
  10. package/dist/cjs/index-1DobCKAF.d.cts +160 -0
  11. package/dist/cjs/index.cjs +138 -138
  12. package/dist/cjs/index.d.cts +5 -3
  13. package/dist/cjs/internal/index.cjs +1 -1
  14. package/dist/cjs/internal/index.d.cts +98 -4
  15. package/dist/cjs/jsx-dev-runtime.cjs +1 -1
  16. package/dist/cjs/jsx-runtime.cjs +1 -1
  17. package/dist/cjs/langchain/index.cjs +1 -0
  18. package/dist/cjs/langchain/index.d.cts +106 -0
  19. package/dist/cjs/servers/express.cjs +138 -138
  20. package/dist/cjs/servers/express.d.cts +6 -4
  21. package/dist/cjs/servers/h3.cjs +137 -137
  22. package/dist/cjs/servers/h3.d.cts +6 -4
  23. package/dist/cjs/servers/lambda.cjs +137 -137
  24. package/dist/cjs/servers/lambda.d.cts +6 -5
  25. package/dist/cjs/servers/nest.cjs +138 -138
  26. package/dist/cjs/servers/nest.d.cts +21 -7
  27. package/dist/cjs/servers/next.cjs +137 -137
  28. package/dist/cjs/servers/next.d.cts +6 -4
  29. package/dist/cjs/servers/nuxt.cjs +137 -137
  30. package/dist/cjs/servers/nuxt.d.cts +6 -4
  31. package/dist/cjs/servers/remix.cjs +137 -137
  32. package/dist/cjs/servers/remix.d.cts +6 -4
  33. package/dist/cjs/servers/sveltekit.cjs +137 -137
  34. package/dist/cjs/servers/sveltekit.d.cts +6 -4
  35. package/dist/cjs/step-resolver.cjs +1 -1
  36. package/dist/cjs/step-resolver.d.cts +3 -2
  37. package/dist/cjs/{subscriber.types-PRaHFLJu.d.cts → subscriber.types-DubxcL8q.d.cts} +18 -78
  38. package/dist/cjs/util.types-DaFfsxgy.d.cts +78 -0
  39. package/dist/cjs/validators.cjs +1 -1
  40. package/dist/{cjs/index-BNiIiOE-.d.cts → esm/agent.types-E3sxq6xr.d.ts} +410 -193
  41. package/dist/esm/ai-sdk/index.d.ts +40 -0
  42. package/dist/esm/ai-sdk/index.js +1 -0
  43. package/dist/esm/cards.d.ts +2 -0
  44. package/dist/esm/cards.js +1 -0
  45. package/dist/esm/{chunk-DKQLYSZS.js → chunk-22DBAEUX.js} +1 -1
  46. package/dist/esm/{chunk-KLCBNFAF.js → chunk-3XT6S7TZ.js} +14 -14
  47. package/dist/esm/chunk-3ZWME5KT.js +0 -0
  48. package/dist/esm/chunk-C3P3ZH2I.js +36 -0
  49. package/dist/esm/{chunk-QTWEINJ4.js → chunk-CANJPWLT.js} +1 -1
  50. package/dist/esm/chunk-E2MET7IR.js +1 -0
  51. package/dist/esm/chunk-FHYMHX3T.js +1 -0
  52. package/dist/esm/chunk-Q6BV36LU.js +98 -0
  53. package/dist/esm/chunk-W6DIBD4Z.js +1 -0
  54. package/dist/esm/{health-check.types-C-81VaBO.d.ts → health-check.types-BNocZ4Wk.d.ts} +6 -140
  55. package/dist/esm/index-aVljLqR4.d.ts +160 -0
  56. package/dist/esm/index.d.ts +5 -3
  57. package/dist/esm/index.js +1 -1
  58. package/dist/esm/internal/index.d.ts +98 -4
  59. package/dist/esm/internal/index.js +1 -1
  60. package/dist/esm/jsx-dev-runtime.js +1 -1
  61. package/dist/esm/jsx-runtime-7EPQR56T.js +1 -0
  62. package/dist/esm/jsx-runtime.js +1 -1
  63. package/dist/esm/langchain/index.d.ts +106 -0
  64. package/dist/esm/langchain/index.js +1 -0
  65. package/dist/esm/servers/express.d.ts +6 -4
  66. package/dist/esm/servers/express.js +1 -1
  67. package/dist/esm/servers/h3.d.ts +6 -4
  68. package/dist/esm/servers/h3.js +1 -1
  69. package/dist/esm/servers/lambda.d.ts +6 -5
  70. package/dist/esm/servers/lambda.js +1 -1
  71. package/dist/esm/servers/nest.d.ts +21 -7
  72. package/dist/esm/servers/nest.js +1 -1
  73. package/dist/esm/servers/next.d.ts +6 -4
  74. package/dist/esm/servers/next.js +1 -1
  75. package/dist/esm/servers/nuxt.d.ts +6 -4
  76. package/dist/esm/servers/nuxt.js +1 -1
  77. package/dist/esm/servers/remix.d.ts +6 -4
  78. package/dist/esm/servers/remix.js +1 -1
  79. package/dist/esm/servers/sveltekit.d.ts +6 -4
  80. package/dist/esm/servers/sveltekit.js +1 -1
  81. package/dist/esm/step-resolver.d.ts +3 -2
  82. package/dist/esm/step-resolver.js +1 -1
  83. package/dist/esm/{subscriber.types-CC7F0JO_.d.ts → subscriber.types-CmybpXhA.d.ts} +18 -78
  84. package/dist/esm/util.types-DaFfsxgy.d.ts +78 -0
  85. package/dist/esm/validators.js +1 -1
  86. package/package.json +56 -8
  87. package/dist/esm/chunk-52LSX2V5.js +0 -1
  88. package/dist/esm/chunk-N4PZBM53.js +0 -1
  89. package/dist/esm/chunk-ND6IQFTA.js +0 -133
@@ -1,8 +1,198 @@
1
1
  import { ChatElement, Emoji, CardElement } from 'chat';
2
- import { T as TriggerRecipientsPayload, L as Logger, C as ClientOptions, W as Workflow, H as HealthCheck, e as DiscoverOutput, i as Event, n as ExecuteOutput, d as CodeResult, j as EventTriggerParams, m as Execute, t as WorkflowOptions } from './health-check.types-D4NEyjyC.cjs';
3
- import { g as Awaitable } from './subscriber.types-PRaHFLJu.cjs';
4
- import { S as Schema, a as FromSchema, F as FromSchemaUnvalidated } from './base.schema.types-BApIn9jr.cjs';
5
- import './step-resolver.cjs';
2
+ import { A as Awaitable } from './util.types-DaFfsxgy.js';
3
+
4
+ type AgentErrorDelivery = {
5
+ statusCode: number;
6
+ responseBody: string;
7
+ };
8
+ type AgentErrorOptions = {
9
+ cause?: unknown;
10
+ delivery?: AgentErrorDelivery;
11
+ };
12
+ /** Turn and handler failures. Delivery failures use {@link AgentDeliveryError}. */
13
+ declare class AgentError extends Error {
14
+ readonly cause?: unknown;
15
+ readonly delivery?: AgentErrorDelivery;
16
+ constructor(message: string, options?: AgentErrorOptions);
17
+ }
18
+ /**
19
+ * Thrown by `ctx.reply()` and `handle.edit()` when the upstream message delivery
20
+ * fails — e.g. the configured email provider returns 401, Slack rejects the token,
21
+ * or Teams rejects the request.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { AgentDeliveryError } from '@novu/framework';
26
+ *
27
+ * try {
28
+ * await ctx.reply('Hello!');
29
+ * } catch (err) {
30
+ * if (err instanceof AgentDeliveryError) {
31
+ * console.error('Delivery failed:', err.message, err.statusCode);
32
+ * return;
33
+ * }
34
+ * throw err;
35
+ * }
36
+ * ```
37
+ */
38
+ declare class AgentDeliveryError extends AgentError {
39
+ readonly statusCode: number;
40
+ readonly responseBody: string;
41
+ constructor(statusCode: number, responseBody: string);
42
+ }
43
+ declare function toAgentError(err: unknown): AgentError;
44
+
45
+ declare enum ChannelTypeEnum {
46
+ IN_APP = "in_app",
47
+ EMAIL = "email",
48
+ SMS = "sms",
49
+ CHAT = "chat",
50
+ PUSH = "push"
51
+ }
52
+ interface IAttachmentOptions {
53
+ mime: string;
54
+ file: Buffer;
55
+ name?: string;
56
+ channels?: ChannelTypeEnum[];
57
+ cid?: string;
58
+ disposition?: string;
59
+ }
60
+ interface ITriggerPayload {
61
+ attachments?: IAttachmentOptions[];
62
+ [key: string]: string | string[] | boolean | number | undefined | IAttachmentOptions | IAttachmentOptions[] | Record<string, unknown>;
63
+ }
64
+ interface ISubscriberPayload {
65
+ subscriberId: string;
66
+ firstName?: string;
67
+ lastName?: string;
68
+ email?: string;
69
+ phone?: string;
70
+ avatar?: string;
71
+ locale?: string;
72
+ data?: Record<string, unknown>;
73
+ channels?: ISubscriberChannel[];
74
+ }
75
+ interface ISubscriberChannel {
76
+ providerId: ChatProviderIdEnum | PushProviderIdEnum;
77
+ integrationIdentifier?: string;
78
+ credentials: IChannelCredentials;
79
+ }
80
+ interface IChannelCredentials {
81
+ webhookUrl?: string;
82
+ deviceTokens?: string[];
83
+ }
84
+ interface ITopic {
85
+ type: 'Topic';
86
+ topicKey: string;
87
+ exclude?: string[];
88
+ }
89
+ type TriggerRecipientsPayload = string | ISubscriberPayload | ITopic | ISubscriberPayload[] | ITopic[];
90
+ declare enum TriggerEventStatusEnum {
91
+ ERROR = "error",
92
+ NOT_ACTIVE = "trigger_not_active",
93
+ NO_WORKFLOW_ACTIVE_STEPS = "no_workflow_active_steps_defined",
94
+ NO_WORKFLOW_STEPS = "no_workflow_steps_defined",
95
+ PROCESSED = "processed",
96
+ SUBSCRIBER_MISSING = "subscriber_id_missing",
97
+ TENANT_MISSING = "no_tenant_found"
98
+ }
99
+ declare enum ChatProviderIdEnum {
100
+ Slack = "slack",
101
+ Discord = "discord",
102
+ MsTeams = "msteams",
103
+ Mattermost = "mattermost",
104
+ Ryver = "ryver",
105
+ Zulip = "zulip",
106
+ GrafanaOnCall = "grafana-on-call",
107
+ GetStream = "getstream",
108
+ RocketChat = "rocket-chat",
109
+ WhatsAppBusiness = "whatsapp-business",
110
+ ChatWebhook = "chat-webhook",
111
+ Telegram = "telegram",
112
+ Sendblue = "sendblue"
113
+ }
114
+ declare enum PushProviderIdEnum {
115
+ FCM = "fcm",
116
+ APNS = "apns",
117
+ EXPO = "expo",
118
+ OneSignal = "one-signal",
119
+ Pushpad = "pushpad",
120
+ PushWebhook = "push-webhook",
121
+ PusherBeams = "pusher-beams",
122
+ AppIO = "appio"
123
+ }
124
+ /**
125
+ * A preference for a notification delivery workflow.
126
+ *
127
+ * This provides a shortcut to setting all channels to the same preference.
128
+ */
129
+ type WorkflowPreference = {
130
+ /**
131
+ * A flag specifying if notification delivery is enabled for the workflow.
132
+ *
133
+ * If `true`, notification delivery is enabled by default for all channels.
134
+ *
135
+ * This setting can be overridden by the channel preferences.
136
+ *
137
+ * @default true
138
+ */
139
+ enabled: boolean;
140
+ /**
141
+ * A flag specifying if the preference is read-only.
142
+ *
143
+ * If `true`, the preference cannot be changed by the Subscriber.
144
+ *
145
+ * @default false
146
+ */
147
+ readOnly: boolean;
148
+ };
149
+ /** A preference for a notification delivery channel. */
150
+ type ChannelPreference = {
151
+ /**
152
+ * A flag specifying if notification delivery is enabled for the channel.
153
+ *
154
+ * If `true`, notification delivery is enabled.
155
+ *
156
+ * @default true
157
+ */
158
+ enabled: boolean;
159
+ };
160
+ type WorkflowPreferences = {
161
+ /**
162
+ * A preference for the workflow.
163
+ *
164
+ * The values specified here will be used if no preference is specified for a channel.
165
+ */
166
+ all: WorkflowPreference;
167
+ /**
168
+ * A preference for each notification delivery channel.
169
+ *
170
+ * If no preference is specified for a channel, the `all` preference will be used.
171
+ */
172
+ channels: Record<ChannelTypeEnum, ChannelPreference>;
173
+ };
174
+ /**
175
+ * Recursively make all properties of type `T` optional.
176
+ */
177
+ type DeepPartial<T> = T extends object ? {
178
+ [P in keyof T]?: DeepPartial<T[P]>;
179
+ } : T;
180
+ /** A partial set of workflow preferences. */
181
+ type WorkflowPreferencesPartial = DeepPartial<WorkflowPreferences>;
182
+
183
+ type Verdict = 'approve' | 'deny';
184
+ interface ToolApprovalRequestPayload {
185
+ approvalId: string;
186
+ toolCallId: string;
187
+ name: string;
188
+ input?: Record<string, unknown>;
189
+ }
190
+ interface ParsedApprovalAction {
191
+ approved: boolean;
192
+ approvalId: string;
193
+ }
194
+ declare function buildApprovalActionId(verdict: Verdict, approvalId: string): string;
195
+ declare function parseApprovalActionId(id: string | undefined): ParsedApprovalAction | null;
6
196
 
7
197
  declare enum AgentEventEnum {
8
198
  ON_MESSAGE = "onMessage",
@@ -64,6 +254,24 @@ interface AgentSubscriber {
64
254
  /** Arbitrary custom data attached to the subscriber in Novu. */
65
255
  data?: Record<string, unknown>;
66
256
  }
257
+ /**
258
+ * Tool-call details on a tool-related history entry. Which fields are set depends on the
259
+ * entry's `type` (`tool_approval_request`, `tool_approval_decision`, or `tool_result`).
260
+ */
261
+ interface AgentToolData {
262
+ /** Id of the tool call. */
263
+ toolCallId?: string;
264
+ /** Name of the tool. */
265
+ toolName?: string;
266
+ /** Id linking an approval request to its decision. */
267
+ approvalId?: string;
268
+ /** Arguments the tool was called with. */
269
+ input?: Record<string, unknown>;
270
+ /** Whether the tool call was approved or denied. */
271
+ approved?: boolean;
272
+ /** What the tool returned (or the `execution-denied` marker for a denied call). */
273
+ output?: unknown;
274
+ }
67
275
  /**
68
276
  * A single entry in the conversation history.
69
277
  * `ctx.history` is an ordered array of these entries — map them to your LLM's
@@ -72,17 +280,22 @@ interface AgentSubscriber {
72
280
  interface AgentHistoryEntry {
73
281
  /** Message role: `'user'`, `'assistant'`, or `'system'`. */
74
282
  role: string;
75
- /** Content type: `'text'`, `'card'`, etc. */
283
+ /**
284
+ * The kind of entry: `'message'`, `'edit'`, `'signal'`, or a tool-lifecycle event
285
+ * (`'tool_approval_request'`, `'tool_approval_decision'`, `'tool_result'`).
286
+ */
76
287
  type: string;
77
288
  /** Plain-text representation of the message content. */
78
289
  content: string;
79
290
  richContent?: Record<string, unknown>;
80
291
  senderName?: string;
81
- /** Present on system entries that carry a Novu signal (e.g. metadata updates). */
292
+ /** Structured data for `signal` entries (e.g. metadata updates). */
82
293
  signalData?: {
83
294
  type: string;
84
295
  payload?: Record<string, unknown>;
85
296
  };
297
+ /** Tool-call details — set on tool-lifecycle entries (`tool_*`). */
298
+ toolData?: AgentToolData;
86
299
  createdAt: string;
87
300
  }
88
301
  /** Resolved inbound email domain metadata (present when `platform === 'email'`). */
@@ -150,6 +363,7 @@ type MessageContent = string | ChatElement;
150
363
  interface ReplyContent {
151
364
  markdown?: string;
152
365
  card?: CardElement;
366
+ toolApprovalCard?: ToolApprovalCard;
153
367
  files?: FileRef[];
154
368
  }
155
369
  /**
@@ -166,6 +380,89 @@ interface AgentAction {
166
380
  /** Platform-native message ID of the message containing the clicked button/action. */
167
381
  sourceMessageId?: string;
168
382
  }
383
+ /**
384
+ * A tool call that requires user approval before it runs.
385
+ * Pass this to `ctx.toolApproval.request()` when your agent wants to gate a tool.
386
+ */
387
+ interface AgentToolCall {
388
+ id: string;
389
+ name: string;
390
+ input?: Record<string, unknown>;
391
+ }
392
+ /**
393
+ * Presentation descriptor for Novu's built-in tool-approval card. Returned by the
394
+ * injected `approvalCard()` helper. Novu renders it natively on Slack and as a
395
+ * portable fallback elsewhere; Approve/Deny action ids are always supplied by Novu.
396
+ *
397
+ * Channel mapping (self-hosted):
398
+ * - **Slack (native card):** `icon`, `title`, `subtitle`, `body`, `approveLabel`, `denyLabel`
399
+ * - **Other channels (portable card):** `title`, `subtitle`, `approveLabel`, `denyLabel`
400
+ *
401
+ * `icon` and `body` are ignored on non-Slack channels — the portable fallback has no
402
+ * card image or body block. Catalog icons are 32×32; custom `https://` URLs should match.
403
+ */
404
+ interface ToolApprovalCard {
405
+ type: 'tool-approval-card';
406
+ /** Slack only. Catalog id (`'stripe'`), `https://` URL, or omit to auto-match the tool name. Use 32×32 px for custom URLs. */
407
+ icon?: string;
408
+ /** All channels. Card title. Defaults to `Tool approval required`. */
409
+ title?: string;
410
+ /** All channels. Card subtitle; auto-generated from the tool name/input when omitted. */
411
+ subtitle?: string;
412
+ /** Slack only. Optional markdown body (e.g. argument preview). Not shown on portable fallback. */
413
+ body?: string;
414
+ /** All channels. Approve button label. Defaults to `Approve`. */
415
+ approveLabel?: string;
416
+ /** All channels. Deny button label. Defaults to `Deny`. */
417
+ denyLabel?: string;
418
+ }
419
+ /**
420
+ * Returned by `ctx.toolApproval.request()`. Return it from `onMessage` to post
421
+ * an approval card and end the turn until the user approves or denies.
422
+ */
423
+ declare class PendingApproval {
424
+ readonly __novuPendingApproval: true;
425
+ }
426
+ /** Optional customization for tool approval messages. */
427
+ interface ToolApprovalConfig {
428
+ /**
429
+ * Build the approval message shown while waiting for a decision.
430
+ *
431
+ * Return `approvalCard(...)` for Novu's channel-adaptive card (native on Slack,
432
+ * fallback elsewhere), or return a string/`Card` to take full control (portable
433
+ * on every channel). Use the provided `actionIds` on your own buttons.
434
+ */
435
+ renderApproval?: (args: {
436
+ toolCall: AgentToolCall;
437
+ actionIds: {
438
+ approve: string;
439
+ deny: string;
440
+ };
441
+ approvalCard: (overrides?: Omit<ToolApprovalCard, 'type'>) => ToolApprovalCard;
442
+ }) => MessageContent | ToolApprovalCard;
443
+ }
444
+ /** Passed to `onToolApproval` when the user clicks Approve or Deny. */
445
+ interface ToolApprovalDecision {
446
+ /** The tool that was awaiting approval. */
447
+ toolCall: AgentToolCall;
448
+ /** `true` if the user approved, `false` if they denied. */
449
+ approved: boolean;
450
+ /**
451
+ * Handle to the approval message. When you register `onToolApproval`, you own
452
+ * card cleanup — call `edit()` or `delete()` as needed. When the framework
453
+ * handles the approval click, the card is deleted for you.
454
+ */
455
+ approvalMessage: ReplyHandle;
456
+ }
457
+ /** Controls on `ctx.toolApproval` for gating tool calls. */
458
+ interface ToolApprovalControl {
459
+ /**
460
+ * Post an approval message and pause the turn.
461
+ * Return the result (`return ctx.toolApproval.request(...)`) from `onMessage`
462
+ * to end the turn until the user decides.
463
+ */
464
+ request(toolCall: AgentToolCall): Promise<PendingApproval>;
465
+ }
169
466
  /** An emoji reaction added to or removed from a message. */
170
467
  interface AgentReaction {
171
468
  /** Platform-native ID of the message that was reacted to. */
@@ -192,8 +489,10 @@ interface ReplyHandle {
192
489
  edit(content: MessageContent, options?: {
193
490
  files?: FileRef[];
194
491
  }): Promise<ReplyHandle>;
492
+ /** Delete this message from the platform. Removes the rendered message only — history is preserved. */
493
+ delete(): Promise<void>;
195
494
  }
196
- interface AgentContextBase {
495
+ interface AgentHandlerContext {
197
496
  /** Live state of the current conversation, including persisted metadata. */
198
497
  readonly conversation: AgentConversation;
199
498
  /**
@@ -227,6 +526,15 @@ interface AgentContextBase {
227
526
  reply(content: MessageContent, options?: {
228
527
  files?: FileRef[];
229
528
  }): Promise<ReplyHandle>;
529
+ /**
530
+ * Gate tool calls that need user approval before they run.
531
+ *
532
+ * @example
533
+ * if (needsApproval(toolCall)) {
534
+ * return ctx.toolApproval.request(toolCall);
535
+ * }
536
+ */
537
+ readonly toolApproval: ToolApprovalControl;
230
538
  /**
231
539
  * Mark the conversation as resolved. Optionally provide a summary for the resolution record.
232
540
  * Triggers the `onResolve` handler if one is registered.
@@ -285,25 +593,49 @@ interface AgentContextBase {
285
593
  * await ctx.reply('Done!');
286
594
  */
287
595
  addReaction(messageId: string, emojiName: Emoji): void;
596
+ /**
597
+ * Delete a platform message by id. Queued and flushed with the next `ctx.reply()`,
598
+ * or automatically when the handler completes (same batching as `ctx.addReaction()`).
599
+ * Deletes the rendered message only — conversation history is preserved.
600
+ *
601
+ * @example
602
+ * ctx.deleteMessage(ctx.action!.sourceMessageId!);
603
+ * await ctx.reply('Processing…');
604
+ */
605
+ deleteMessage(messageId: string): void;
606
+ /**
607
+ * Control the typing / "Thinking…" status for the current turn.
608
+ * Posts immediately (like `reply()`), updating the indicator Novu already shows on inbound.
609
+ *
610
+ * @example
611
+ * await ctx.typing('Searching the docs…'); // set / replace the status text
612
+ * await ctx.typing(); // reset to the default "Thinking…"
613
+ * await ctx.typing.stop(); // clear it for this turn
614
+ *
615
+ * Behaviour is best-effort per platform: custom text shows on Slack-like platforms,
616
+ * a generic typing bubble on others, and is a no-op where there is no typing channel
617
+ * (e.g. email). A normal turn that ends with `ctx.reply()` clears the status automatically.
618
+ */
619
+ typing: TypingControl;
288
620
  }
289
621
  /** Context passed to the `onMessage` handler. */
290
- interface AgentMessageContext extends AgentContextBase {
622
+ interface AgentMessageContext extends AgentHandlerContext {
291
623
  readonly event: 'onMessage';
292
624
  }
293
625
  /** Context passed to the `onAction` handler. */
294
- interface AgentActionContext extends AgentContextBase {
626
+ interface AgentActionContext extends AgentHandlerContext {
295
627
  readonly event: 'onAction';
296
628
  /** The button click or interactive action that triggered this handler. */
297
629
  readonly action: AgentAction;
298
630
  }
299
631
  /** Context passed to the `onReaction` handler. */
300
- interface AgentReactionContext extends AgentContextBase {
632
+ interface AgentReactionContext extends AgentHandlerContext {
301
633
  readonly event: 'onReaction';
302
634
  /** The emoji reaction that triggered this handler. */
303
635
  readonly reaction: AgentReaction;
304
636
  }
305
637
  /** Context passed to the `onResolve` handler. */
306
- interface AgentResolveContext extends AgentContextBase {
638
+ interface AgentResolveContext extends AgentHandlerContext {
307
639
  readonly event: 'onResolve';
308
640
  }
309
641
  type AgentContext = AgentMessageContext | AgentActionContext | AgentReactionContext | AgentResolveContext;
@@ -346,10 +678,41 @@ interface AgentHandlers {
346
678
  * `ctx.subscriber` and `ctx.conversation`.
347
679
  */
348
680
  onResolve?: (ctx: AgentResolveContext) => Awaitable<MessageContent | void>;
681
+ /**
682
+ * Fires when the user approves or denies a tool call you previously gated with
683
+ * `ctx.toolApproval.request()`.
684
+ *
685
+ * @param decision - The tool, the user's verdict, and a handle to the approval message.
686
+ * @param ctx - Conversation context (history, subscriber, metadata, reply/trigger methods).
687
+ *
688
+ * Run the tool (or skip it), then return a reply or call `ctx.reply()` directly.
689
+ * Register this handler whenever you call `ctx.toolApproval.request()` in `onMessage`.
690
+ */
691
+ onToolApproval?: (decision: ToolApprovalDecision, ctx: AgentActionContext) => Awaitable<MessageContent | void>;
692
+ /**
693
+ * Optional turn failure handler. Return `{ suppress: true }` to skip user notification,
694
+ * return message content for a custom user reply, or return nothing to auto-report
695
+ * `{ error: true }` to Novu (generic end-user copy delivered by the API).
696
+ */
697
+ onError?: (error: AgentError, ctx: AgentMessageContext | AgentActionContext | AgentReactionContext | AgentResolveContext) => Awaitable<AgentErrorResult>;
698
+ /**
699
+ * Customize how approval messages look. Omit to use the built-in Approve/Deny card.
700
+ */
701
+ toolApproval?: ToolApprovalConfig;
349
702
  }
703
+ type AgentErrorSuppress = {
704
+ suppress: true;
705
+ };
706
+ type AgentErrorResult = MessageContent | void | AgentErrorSuppress;
350
707
  interface Agent {
351
708
  id: string;
352
709
  handlers: AgentHandlers;
710
+ /**
711
+ * @internal Set by `agent()` / ai-sdk registration. True when the application
712
+ * author registered `onToolApproval` (full manual card cleanup). False when
713
+ * only a framework wrapper handles approval clicks (auto-delete after handler).
714
+ */
715
+ userOnToolApproval?: boolean;
353
716
  }
354
717
  interface AgentBridgeRequest {
355
718
  version: number;
@@ -398,6 +761,17 @@ type TriggerSignal = {
398
761
  payload?: Record<string, unknown>;
399
762
  };
400
763
  type Signal = MetadataSignal | TriggerSignal;
764
+ /** The outcome of a tool call, reported back so it's saved in the conversation history. */
765
+ type ToolResult = {
766
+ /** Id of the tool call this result belongs to. */
767
+ toolCallId: string;
768
+ /** Name of the tool. */
769
+ toolName?: string;
770
+ /** What the tool returned (or the `execution-denied` marker for a denied call). */
771
+ output: unknown;
772
+ /** Optional human-readable summary shown in the conversation timeline. */
773
+ preview?: string;
774
+ };
401
775
  /** In-place edit of a previously posted agent message. Identified by platform message id. */
402
776
  interface EditPayload {
403
777
  messageId: string;
@@ -408,6 +782,24 @@ interface AddReactionPayload {
408
782
  messageId: string;
409
783
  emojiName: Emoji;
410
784
  }
785
+ /** Delete a previously posted platform message. Removes the rendered message only — history is preserved. */
786
+ interface DeleteMessagePayload {
787
+ messageId: string;
788
+ }
789
+ /**
790
+ * Per-turn typing/status control op sent on the reply contract.
791
+ * - `{ status?: string }` — set/replace the status; omit `status` for the default "Thinking…".
792
+ * - `'stop'` — clear the status for this turn.
793
+ */
794
+ type TypingOp = {
795
+ status?: string;
796
+ } | 'stop';
797
+ /**
798
+ * `ctx.typing` surface: a callable that sets/updates the status, plus `.stop()` to clear it.
799
+ */
800
+ type TypingControl = ((status?: string) => Promise<void>) & {
801
+ stop: () => Promise<void>;
802
+ };
411
803
  interface AgentReplyPayload {
412
804
  conversationId: string;
413
805
  integrationIdentifier: string;
@@ -417,7 +809,13 @@ interface AgentReplyPayload {
417
809
  summary?: string;
418
810
  };
419
811
  signals?: Signal[];
812
+ toolResults?: ToolResult[];
813
+ toolApprovalRequest?: ToolApprovalRequestPayload;
420
814
  addReactions?: AddReactionPayload[];
815
+ deleteMessages?: DeleteMessagePayload[];
816
+ typing?: TypingOp;
817
+ /** Bridge reports a real turn failure; Novu delivers generic user copy. */
818
+ error?: true;
421
819
  }
422
820
  /** Shape returned by /agents/:id/reply when a reply or edit was delivered. */
423
821
  interface SentMessageInfo {
@@ -425,185 +823,4 @@ interface SentMessageInfo {
425
823
  platformThreadId: string;
426
824
  }
427
825
 
428
- /**
429
- * Thrown by `ctx.reply()` and `handle.edit()` when the upstream message delivery
430
- * fails — e.g. the configured email provider returns 401, Slack rejects the token,
431
- * or Teams rejects the request.
432
- *
433
- * `message` is always a short, human-readable summary.
434
- * `responseBody` preserves the raw upstream body for debugging.
435
- *
436
- * @example
437
- * ```ts
438
- * import { AgentDeliveryError } from '@novu/framework';
439
- *
440
- * try {
441
- * await ctx.reply('Hello!');
442
- * } catch (err) {
443
- * if (err instanceof AgentDeliveryError) {
444
- * console.error('Delivery failed:', err.message);
445
- * return;
446
- * }
447
- * throw err;
448
- * }
449
- * ```
450
- */
451
- declare class AgentDeliveryError extends Error {
452
- readonly statusCode: number;
453
- readonly responseBody: string;
454
- constructor(statusCode: number, responseBody: string);
455
- }
456
-
457
- /**
458
- * Define a new conversational agent.
459
- *
460
- * @param agentId - Unique identifier matching the agent entity created in Novu (e.g. 'wine-bot')
461
- * @param handlers - Handler functions for agent events
462
- */
463
- declare function agent(agentId: string, handlers: AgentHandlers): Agent;
464
-
465
- declare class Client {
466
- private discoveredWorkflows;
467
- private discoverWorkflowPromises;
468
- private registeredAgents;
469
- private templateEngine;
470
- secretKey: string;
471
- apiUrl: string;
472
- version: string;
473
- strictAuthentication: boolean;
474
- verbose: boolean;
475
- logger: Logger;
476
- constructor(options?: ClientOptions);
477
- private buildOptions;
478
- private log;
479
- /**
480
- * Adds workflows to the client.
481
- *
482
- * A locking mechanism is used to ensure that duplicate workflows are not added.
483
- *
484
- * @param workflows - The workflows to add.
485
- */
486
- addWorkflows(workflows: Array<Workflow>): Promise<void>;
487
- addAgents(agents: Array<Agent>): void;
488
- getAgent(agentId: string): Agent | undefined;
489
- private addWorkflow;
490
- healthCheck(): HealthCheck;
491
- private getWorkflow;
492
- private getStep;
493
- private getRegisteredWorkflows;
494
- discover(): DiscoverOutput;
495
- /**
496
- * Mocks data based on the given schema.
497
- * The `default` value in the schema is used as the base data.
498
- * If no `default` value is provided, the data is generated using JSONSchemaFaker.
499
- *
500
- * @param schema
501
- * @returns mocked data
502
- */
503
- private mock;
504
- private validate;
505
- private throwInvalidProvider;
506
- private throwInvalidStep;
507
- private throwInvalidEvent;
508
- private executeStepFactory;
509
- private shouldSanitize;
510
- private shouldSkip;
511
- executeWorkflow(event: Event): Promise<ExecuteOutput>;
512
- private createExecutionPayload;
513
- private prettyPrintExecute;
514
- private executeProviders;
515
- private previewProvider;
516
- private executeProvider;
517
- private executeStep;
518
- private compileControls;
519
- /**
520
- * Preprocesses standalone translation patterns.
521
- * Transforms {{t.key}} to [T:key] placeholder (not Liquid syntax, passes through unchanged).
522
- */
523
- private preprocessTranslationPatterns;
524
- /**
525
- * Preprocesses translation keys used as filter arguments.
526
- * Transforms 't.key' to '[T:key]' placeholder (not Liquid syntax, passes through unchanged).
527
- * Example: pluralize: 't.apple', 't.apples' → pluralize: '[T:apple]', '[T:apples]'
528
- */
529
- private preprocessFilterTranslationArgs;
530
- /**
531
- * Post-processes placeholders back to translation markers after Liquid render.
532
- * Transforms [T:key] back to {{t.key}} for the translation service.
533
- */
534
- private postprocessTranslationMarkers;
535
- /**
536
- * Create the controls for a step, taking both the event controls and the default controls into account
537
- *
538
- * @param step The step to create the controls for
539
- * @param event The event that triggered the step
540
- * @returns The controls for the step
541
- */
542
- private createStepControls;
543
- private previewStep;
544
- private constructStepForPreview;
545
- private extractMockDataForPreviousSteps;
546
- private previewRequiredStep;
547
- private getStepState;
548
- private getStepCode;
549
- private getWorkflowCode;
550
- getCode(workflowId: string, stepId?: string): CodeResult;
551
- }
552
-
553
- interface ServeHandlerOptions {
554
- client?: Client;
555
- workflows?: Array<Workflow>;
556
- agents?: Array<Agent>;
557
- }
558
- type INovuRequestHandlerOptions<Input extends any[] = any[], Output = any> = ServeHandlerOptions & {
559
- frameworkName: string;
560
- client?: Client;
561
- workflows?: Array<Workflow>;
562
- agents?: Array<Agent>;
563
- handler: Handler<Input, Output>;
564
- };
565
- type Handler<Input extends any[] = any[], Output = any> = (...args: Input) => HandlerResponse<Output>;
566
- type HandlerResponse<Output = any> = {
567
- body: () => Awaitable<any>;
568
- headers: (key: string) => Awaitable<string | null | undefined>;
569
- method: () => Awaitable<string>;
570
- queryString?: (key: string, url: URL) => Awaitable<string | null | undefined>;
571
- url: () => Awaitable<URL>;
572
- transformResponse: (res: IActionResponse<string>) => Output;
573
- waitUntil?: (promise: Promise<unknown>) => void;
574
- };
575
- type IActionResponse<TBody extends string = string> = {
576
- status: number;
577
- headers: Record<string, string>;
578
- body: TBody;
579
- };
580
- declare class NovuRequestHandler<Input extends any[] = any[], Output = any> {
581
- readonly frameworkName: string;
582
- readonly handler: Handler<Input, Output>;
583
- readonly client: Client;
584
- private readonly hmacEnabled;
585
- private readonly http;
586
- private readonly workflows;
587
- private readonly agents;
588
- constructor(options: INovuRequestHandlerOptions<Input, Output>);
589
- createHandler(): (...args: Input) => Promise<Output>;
590
- private getStaticHeaders;
591
- private createResponse;
592
- private createError;
593
- private handleAction;
594
- private getPostActionMap;
595
- triggerAction(triggerEvent: EventTriggerParams): () => Promise<IActionResponse<string>>;
596
- private getGetActionMap;
597
- private handlePostAction;
598
- private handleGetAction;
599
- private runAgentHandler;
600
- private handleError;
601
- private validateHmac;
602
- }
603
-
604
- /**
605
- * Define a new notification workflow.
606
- */
607
- declare function workflow<T_PayloadSchema extends Schema, T_ControlSchema extends Schema, T_EnvSchema extends Schema, T_PayloadValidated extends Record<string, unknown> = FromSchema<T_PayloadSchema>, T_PayloadUnvalidated extends Record<string, unknown> = FromSchemaUnvalidated<T_PayloadSchema>, T_Controls extends Record<string, unknown> = FromSchema<T_ControlSchema>, T_Env extends Record<string, unknown> = FromSchema<T_EnvSchema>>(workflowId: string, execute: Execute<T_PayloadValidated, T_Controls, T_Env>, workflowOptions?: WorkflowOptions<T_PayloadSchema, T_ControlSchema, T_EnvSchema>): Workflow<T_PayloadUnvalidated>;
608
-
609
- export { type Agent as A, Client as C, type EditPayload as E, type FileRef as F, type INovuRequestHandlerOptions as I, type MessageContent as M, NovuRequestHandler as N, type ReplyContent as R, type ServeHandlerOptions as S, type TriggerSignal as T, type AgentAction as a, type AgentActionContext as b, type AgentAttachment as c, type AgentBridgeRequest as d, type AgentContext as e, type AgentConversation as f, AgentDeliveryError as g, AgentEventEnum as h, type AgentHandlers as i, type AgentHistoryEntry as j, type AgentMessage as k, type AgentMessageAuthor as l, type AgentMessageContext as m, type AgentPlatformContext as n, type AgentReaction as o, type AgentReactionContext as p, type AgentReplyPayload as q, type AgentResolveContext as r, type AgentSubscriber as s, type MetadataSignal as t, type ReplyHandle as u, type SentMessageInfo as v, type Signal as w, agent as x, workflow as y };
826
+ export { type Agent as A, type ToolApprovalRequestPayload as B, AgentEventEnum as C, type TypingControl as D, type ToolApprovalControl as E, type FileRef as F, type AgentBridgeRequest as G, type AgentReplyPayload as H, type ITriggerPayload as I, type DeleteMessagePayload as J, type EditPayload as K, type MetadataSignal as L, type MessageContent as M, type ReplyContent as N, type Signal as O, type ParsedApprovalAction as P, type TriggerSignal as Q, type ReplyHandle as R, type SentMessageInfo as S, type ToolApprovalCard as T, buildApprovalActionId as U, parseApprovalActionId as V, type WorkflowPreferencesPartial as W, type AgentAction as a, type AgentActionContext as b, type AgentAttachment as c, type AgentContext as d, type AgentConversation as e, AgentDeliveryError as f, AgentError as g, type AgentHandlerContext as h, type AgentHandlers as i, type AgentHistoryEntry as j, type AgentMessage as k, type AgentMessageAuthor as l, type AgentMessageContext as m, type AgentPlatformContext as n, type AgentReaction as o, type AgentReactionContext as p, type AgentResolveContext as q, type AgentSubscriber as r, type AgentToolCall as s, type ToolApprovalConfig as t, type ToolApprovalDecision as u, toAgentError as v, type TriggerRecipientsPayload as w, type ISubscriberPayload as x, TriggerEventStatusEnum as y, type ToolResult as z };