@msg91comm/m91 1.1.0 → 1.2.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 (66) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +241 -19
  3. package/dist/src/index.d.ts +1 -1
  4. package/dist/src/index.js +1 -1
  5. package/dist/src/integrations/claude-agent-sdk.d.ts +63 -0
  6. package/dist/src/integrations/claude-agent-sdk.d.ts.map +1 -0
  7. package/dist/src/integrations/claude-agent-sdk.js +109 -0
  8. package/dist/src/integrations/claude-agent-sdk.js.map +1 -0
  9. package/dist/src/integrations/google-adk.d.ts +47 -0
  10. package/dist/src/integrations/google-adk.d.ts.map +1 -0
  11. package/dist/src/integrations/google-adk.js +84 -0
  12. package/dist/src/integrations/google-adk.js.map +1 -0
  13. package/dist/src/integrations/index.d.ts +7 -3
  14. package/dist/src/integrations/index.d.ts.map +1 -1
  15. package/dist/src/integrations/index.js +6 -2
  16. package/dist/src/integrations/index.js.map +1 -1
  17. package/dist/src/integrations/langchain.d.ts +56 -5
  18. package/dist/src/integrations/langchain.d.ts.map +1 -1
  19. package/dist/src/integrations/langchain.js +56 -2
  20. package/dist/src/integrations/langchain.js.map +1 -1
  21. package/dist/src/integrations/mastra.d.ts +75 -0
  22. package/dist/src/integrations/mastra.d.ts.map +1 -0
  23. package/dist/src/integrations/mastra.js +109 -0
  24. package/dist/src/integrations/mastra.js.map +1 -0
  25. package/dist/src/integrations/openai-agents.d.ts +61 -0
  26. package/dist/src/integrations/openai-agents.d.ts.map +1 -0
  27. package/dist/src/integrations/openai-agents.js +127 -0
  28. package/dist/src/integrations/openai-agents.js.map +1 -0
  29. package/dist/src/integrations/specs.d.ts +42 -0
  30. package/dist/src/integrations/specs.d.ts.map +1 -1
  31. package/dist/src/integrations/specs.js +61 -0
  32. package/dist/src/integrations/specs.js.map +1 -1
  33. package/dist/src/integrations/vercel-ai.d.ts +44 -6
  34. package/dist/src/integrations/vercel-ai.d.ts.map +1 -1
  35. package/dist/src/integrations/vercel-ai.js +38 -3
  36. package/dist/src/integrations/vercel-ai.js.map +1 -1
  37. package/dist/src/integrations/zod-shapes.d.ts +19 -0
  38. package/dist/src/integrations/zod-shapes.d.ts.map +1 -0
  39. package/dist/src/integrations/zod-shapes.js +44 -0
  40. package/dist/src/integrations/zod-shapes.js.map +1 -0
  41. package/dist/src/platform.d.ts +1 -1
  42. package/dist/src/platform.d.ts.map +1 -1
  43. package/dist/src/platform.js +1 -1
  44. package/dist/src/platform.js.map +1 -1
  45. package/dist/src/url-builder.js +1 -1
  46. package/dist/src/url-builder.js.map +1 -1
  47. package/examples/basic-alert.ts +1 -1
  48. package/examples/claude-agent-sdk.ts +23 -0
  49. package/examples/deep-agents.ts +31 -0
  50. package/examples/google-adk.ts +39 -0
  51. package/examples/mastra.ts +41 -0
  52. package/examples/openai-agents.ts +33 -0
  53. package/examples/webhook-urls.ts +1 -1
  54. package/package.json +63 -11
  55. package/src/index.ts +1 -1
  56. package/src/integrations/claude-agent-sdk.ts +158 -0
  57. package/src/integrations/google-adk.ts +122 -0
  58. package/src/integrations/index.ts +7 -3
  59. package/src/integrations/langchain.ts +109 -6
  60. package/src/integrations/mastra.ts +178 -0
  61. package/src/integrations/openai-agents.ts +173 -0
  62. package/src/integrations/specs.ts +104 -0
  63. package/src/integrations/vercel-ai.ts +59 -7
  64. package/src/integrations/zod-shapes.ts +55 -0
  65. package/src/platform.ts +1 -1
  66. package/src/url-builder.ts +1 -1
@@ -1,5 +1,22 @@
1
1
  /**
2
- * Vercel AI SDK tools.
2
+ * Vercel AI SDK: two tools, and a gate for your own tools.
3
+ *
4
+ * **Tools** the model calls when it decides to ask (`createM91Tools`).
5
+ *
6
+ * **Gate** (`createM91ToolApproval`) plugs into AI SDK 6+'s `toolApproval`
7
+ * option: every call to the tools you name rings a phone first, and runs only
8
+ * if a person approves. The model cannot skip it, because it never decides to
9
+ * ask: the SDK asks for it.
10
+ *
11
+ * ```ts
12
+ * const { text } = await generateText({
13
+ * model: 'openai/gpt-5-mini',
14
+ * tools: { issueRefund, deleteUser },
15
+ * toolApproval: createM91ToolApproval(client, { tools: ['issueRefund', 'deleteUser'] }),
16
+ * stopWhen: isStepCount(5),
17
+ * prompt: 'Refund order ORD-40122.',
18
+ * });
19
+ * ```
3
20
  *
4
21
  * ```ts
5
22
  * import { M91Client } from '@msg91comm/m91';
@@ -13,7 +30,7 @@
13
30
  * m91_request_approval: await createM91ApprovalTool(client),
14
31
  * m91_send_alert: await createM91AlertTool(client),
15
32
  * },
16
- * maxSteps: 10,
33
+ * stopWhen: isStepCount(10),
17
34
  * prompt: 'Look into order ORD-40122 and refund it if it checks out.',
18
35
  * });
19
36
  * ```
@@ -23,6 +40,8 @@
23
40
  * dependencies.
24
41
  */
25
42
 
43
+ import type { Tool, ToolApprovalStatus, ToolSet } from 'ai';
44
+
26
45
  import type { M91Client } from '../client.js';
27
46
  import {
28
47
  ALERT_DESCRIPTION,
@@ -33,6 +52,9 @@ import {
33
52
  type AlertDefaults,
34
53
  type ApprovalArgs,
35
54
  type ApprovalDefaults,
55
+ type GateOptions,
56
+ decideToolCall,
57
+ needsHuman,
36
58
  runAlert,
37
59
  runApproval,
38
60
  } from './specs.js';
@@ -63,7 +85,7 @@ async function aiModule(): Promise<AiModule> {
63
85
  export async function createM91ApprovalTool(
64
86
  client: M91Client,
65
87
  defaults: ApprovalDefaults = {},
66
- ): Promise<unknown> {
88
+ ): Promise<Tool<ApprovalArgs, string>> {
67
89
  const { tool, jsonSchema } = await aiModule();
68
90
  return tool({
69
91
  description: APPROVAL_DESCRIPTION,
@@ -71,29 +93,59 @@ export async function createM91ApprovalTool(
71
93
  // and the argument docs stay in one file shared with every other framework.
72
94
  inputSchema: jsonSchema(APPROVAL_SCHEMA),
73
95
  execute: (args: ApprovalArgs) => runApproval(client, args, defaults),
74
- });
96
+ }) as Tool<ApprovalArgs, string>;
75
97
  }
76
98
 
77
99
  /** The fire-and-forget alert. Returns `"sent:delivered=N"` or `"error: …"`. */
78
100
  export async function createM91AlertTool(
79
101
  client: M91Client,
80
102
  defaults: AlertDefaults = {},
81
- ): Promise<unknown> {
103
+ ): Promise<Tool<AlertArgs, string>> {
82
104
  const { tool, jsonSchema } = await aiModule();
83
105
  return tool({
84
106
  description: ALERT_DESCRIPTION,
85
107
  inputSchema: jsonSchema(ALERT_SCHEMA),
86
108
  execute: (args: AlertArgs) => runAlert(client, args, defaults),
87
- });
109
+ }) as Tool<AlertArgs, string>;
88
110
  }
89
111
 
90
112
  /** Both, keyed by tool name, ready to spread into `tools: {…}`. */
91
113
  export async function createM91Tools(
92
114
  client: M91Client,
93
115
  defaults: ApprovalDefaults & AlertDefaults = {},
94
- ): Promise<Record<string, unknown>> {
116
+ ): Promise<ToolSet> {
95
117
  return {
96
118
  m91_request_approval: await createM91ApprovalTool(client, defaults),
97
119
  m91_send_alert: await createM91AlertTool(client, defaults),
98
120
  };
99
121
  }
122
+
123
+ /** The fields of an AI SDK tool call the gate reads. */
124
+ interface ApprovalRequestLike {
125
+ toolCall: { toolName: string; input: unknown; toolCallId: string };
126
+ }
127
+
128
+ /**
129
+ * A `toolApproval` function for `generateText` / `streamText` / `ToolLoopAgent`.
130
+ *
131
+ * Calls to the tools in `options.tools` block while M91 rings a phone, then run
132
+ * if a person approves and come back to the model as denied otherwise. Every
133
+ * other call is `not-applicable` and runs as normal. Not synchronous: it waits
134
+ * up to `timeoutSeconds` (default 300) for an answer.
135
+ */
136
+ export function createM91ToolApproval(
137
+ client: M91Client,
138
+ options: GateOptions & { tools: NonNullable<GateOptions['tools']> },
139
+ ): (request: ApprovalRequestLike) => Promise<ToolApprovalStatus> {
140
+ return async ({ toolCall }) => {
141
+ if (!(await needsHuman(options, toolCall.toolName, toolCall.input))) return 'not-applicable';
142
+ const decision = await decideToolCall(
143
+ client,
144
+ { toolName: toolCall.toolName, input: toolCall.input, callId: toolCall.toolCallId },
145
+ options,
146
+ );
147
+ return decision.approved
148
+ ? { type: 'approved', reason: decision.reason }
149
+ : { type: 'denied', reason: decision.reason };
150
+ };
151
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The approval and alert arguments as zod shapes, for frameworks that only
3
+ * take zod (Claude Agent SDK, Mastra, Google ADK).
4
+ *
5
+ * Built from the zod the caller already has, passed in, so this package never
6
+ * imports zod itself and the descriptions stay the ones in `specs.ts`.
7
+ */
8
+
9
+ import { ALERT_SCHEMA, APPROVAL_SCHEMA } from './specs.js';
10
+
11
+ /** The slice of zod this file uses; v3 and v4 both satisfy it. */
12
+ export interface ZodLike {
13
+ string(): any;
14
+ number(): any;
15
+ enum(values: readonly [string, ...string[]]): any;
16
+ object(shape: Record<string, any>): any;
17
+ }
18
+
19
+ const SEVERITIES = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'] as const;
20
+
21
+ export function approvalShape(z: ZodLike): Record<string, any> {
22
+ const p = APPROVAL_SCHEMA.properties;
23
+ return {
24
+ title: z.string().describe(p.title.description),
25
+ customId: z.string().describe(p.customId.description),
26
+ description: z.string().optional().describe(p.description.description),
27
+ severity: z.enum(SEVERITIES).optional().describe(p.severity.description),
28
+ approveLabel: z.string().optional().describe(p.approveLabel.description),
29
+ rejectLabel: z.string().optional().describe(p.rejectLabel.description),
30
+ timeoutSeconds: z.number().optional().describe(p.timeoutSeconds.description),
31
+ };
32
+ }
33
+
34
+ export function alertShape(z: ZodLike): Record<string, any> {
35
+ const p = ALERT_SCHEMA.properties;
36
+ return {
37
+ title: z.string().describe(p.title.description),
38
+ customId: z.string().optional().describe(p.customId.description),
39
+ description: z.string().optional().describe(p.description.description),
40
+ severity: z.enum(SEVERITIES).optional().describe(p.severity.description),
41
+ };
42
+ }
43
+
44
+ /** zod is an optional peer dependency: import it only when asked, and say so if it is missing. */
45
+ export async function loadZod(framework: string): Promise<ZodLike> {
46
+ try {
47
+ const specifier = 'zod';
48
+ const mod = (await import(specifier)) as { z?: ZodLike } & ZodLike;
49
+ return mod.z ?? mod;
50
+ } catch (error) {
51
+ throw new Error(`The ${framework} integration needs zod. Install it with: npm install zod`, {
52
+ cause: error,
53
+ });
54
+ }
55
+ }
package/src/platform.ts CHANGED
@@ -41,7 +41,7 @@ import {
41
41
  } from './models.js';
42
42
  import { type SendOptions, checkDelivery } from './client.js';
43
43
 
44
- export const DEFAULT_BASE_URL = 'https://m91.msg91.com';
44
+ export const DEFAULT_BASE_URL = 'https://m91-api.msg91.com';
45
45
  export const API_KEY_PREFIX = 'm91sk_';
46
46
 
47
47
  export interface PlatformOptions extends TransportOptions {
@@ -63,7 +63,7 @@ export function normalizeSendLink(sendLink: string): string {
63
63
  // link rather than a mistyped one.
64
64
  if (segments.length < 2 || segments[segments.length - 2] !== 's') {
65
65
  throw new M91ValidationError(
66
- `sendLink must look like https://m91.msg91.com/s/<token> — ` +
66
+ `sendLink must look like https://m91-api.msg91.com/s/<token> — ` +
67
67
  `no /s/<token> found in '${parsed.pathname}'`,
68
68
  );
69
69
  }