@msg91comm/m91 0.0.0-stage → 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.
- package/CHANGELOG.md +94 -0
- package/LICENSE +21 -0
- package/README.md +647 -2
- package/dist/src/client.d.ts +165 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +221 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/exceptions.d.ts +114 -0
- package/dist/src/exceptions.d.ts.map +1 -0
- package/dist/src/exceptions.js +199 -0
- package/dist/src/exceptions.js.map +1 -0
- package/dist/src/http.d.ts +71 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +150 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/index.d.ts +49 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +45 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/integrations/claude-agent-sdk.d.ts +63 -0
- package/dist/src/integrations/claude-agent-sdk.d.ts.map +1 -0
- package/dist/src/integrations/claude-agent-sdk.js +109 -0
- package/dist/src/integrations/claude-agent-sdk.js.map +1 -0
- package/dist/src/integrations/google-adk.d.ts +47 -0
- package/dist/src/integrations/google-adk.d.ts.map +1 -0
- package/dist/src/integrations/google-adk.js +84 -0
- package/dist/src/integrations/google-adk.js.map +1 -0
- package/dist/src/integrations/index.d.ts +26 -0
- package/dist/src/integrations/index.d.ts.map +1 -0
- package/dist/src/integrations/index.js +25 -0
- package/dist/src/integrations/index.js.map +1 -0
- package/dist/src/integrations/langchain.d.ts +94 -0
- package/dist/src/integrations/langchain.d.ts.map +1 -0
- package/dist/src/integrations/langchain.js +129 -0
- package/dist/src/integrations/langchain.js.map +1 -0
- package/dist/src/integrations/langgraph.d.ts +79 -0
- package/dist/src/integrations/langgraph.d.ts.map +1 -0
- package/dist/src/integrations/langgraph.js +114 -0
- package/dist/src/integrations/langgraph.js.map +1 -0
- package/dist/src/integrations/mastra.d.ts +75 -0
- package/dist/src/integrations/mastra.d.ts.map +1 -0
- package/dist/src/integrations/mastra.js +109 -0
- package/dist/src/integrations/mastra.js.map +1 -0
- package/dist/src/integrations/openai-agents.d.ts +61 -0
- package/dist/src/integrations/openai-agents.d.ts.map +1 -0
- package/dist/src/integrations/openai-agents.js +127 -0
- package/dist/src/integrations/openai-agents.js.map +1 -0
- package/dist/src/integrations/specs.d.ts +173 -0
- package/dist/src/integrations/specs.d.ts.map +1 -0
- package/dist/src/integrations/specs.js +217 -0
- package/dist/src/integrations/specs.js.map +1 -0
- package/dist/src/integrations/vercel-ai.d.ts +74 -0
- package/dist/src/integrations/vercel-ai.d.ts.map +1 -0
- package/dist/src/integrations/vercel-ai.js +102 -0
- package/dist/src/integrations/vercel-ai.js.map +1 -0
- package/dist/src/integrations/zod-shapes.d.ts +19 -0
- package/dist/src/integrations/zod-shapes.d.ts.map +1 -0
- package/dist/src/integrations/zod-shapes.js +44 -0
- package/dist/src/integrations/zod-shapes.js.map +1 -0
- package/dist/src/models.d.ts +274 -0
- package/dist/src/models.d.ts.map +1 -0
- package/dist/src/models.js +383 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/platform.d.ts +176 -0
- package/dist/src/platform.d.ts.map +1 -0
- package/dist/src/platform.js +286 -0
- package/dist/src/platform.js.map +1 -0
- package/dist/src/url-builder.d.ts +65 -0
- package/dist/src/url-builder.d.ts.map +1 -0
- package/dist/src/url-builder.js +167 -0
- package/dist/src/url-builder.js.map +1 -0
- package/examples/approval-gate.ts +35 -0
- package/examples/basic-alert.ts +53 -0
- package/examples/claude-agent-sdk.ts +23 -0
- package/examples/deep-agents.ts +31 -0
- package/examples/google-adk.ts +39 -0
- package/examples/langchain-agent.ts +65 -0
- package/examples/langgraph-hitl.ts +70 -0
- package/examples/mastra.ts +41 -0
- package/examples/openai-agents.ts +33 -0
- package/examples/vercel-ai-agent.ts +34 -0
- package/examples/webhook-urls.ts +44 -0
- package/package.json +135 -4
- package/src/client.ts +324 -0
- package/src/exceptions.ts +229 -0
- package/src/http.ts +174 -0
- package/src/index.ts +91 -0
- package/src/integrations/claude-agent-sdk.ts +158 -0
- package/src/integrations/google-adk.ts +122 -0
- package/src/integrations/index.ts +27 -0
- package/src/integrations/langchain.ts +207 -0
- package/src/integrations/langgraph.ts +142 -0
- package/src/integrations/mastra.ts +178 -0
- package/src/integrations/openai-agents.ts +173 -0
- package/src/integrations/specs.ts +328 -0
- package/src/integrations/vercel-ai.ts +151 -0
- package/src/integrations/zod-shapes.ts +55 -0
- package/src/models.ts +660 -0
- package/src/platform.ts +343 -0
- package/src/url-builder.ts +225 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangGraph helpers for the `interrupt()` pattern.
|
|
3
|
+
*
|
|
4
|
+
* LangGraph already knows how to stop a graph and resume it. What it has no
|
|
5
|
+
* opinion about is **how the human finds out it stopped** — and the default
|
|
6
|
+
* answer is that somebody notices a paused run in a web UI. At 2am, for a graph
|
|
7
|
+
* that paused because it wants to move money, nobody does. M91 fills exactly
|
|
8
|
+
* that gap: the graph pauses, a phone rings until somebody answers, and the
|
|
9
|
+
* answer resumes the graph.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here imports `@langchain/langgraph`. These are plain callables you use
|
|
12
|
+
* *around* your graph, so they work the same with `interrupt()`, with
|
|
13
|
+
* `new Command({ resume })`, or with a node you wrote by hand.
|
|
14
|
+
*/
|
|
15
|
+
import { APPROVED, REJECTED } from './specs.js';
|
|
16
|
+
/**
|
|
17
|
+
* Wake a human about an interrupted graph and return what they said.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* const notifier = new M91InterruptNotifier(client);
|
|
21
|
+
*
|
|
22
|
+
* const result = await graph.invoke(state, config);
|
|
23
|
+
* if (result.__interrupt__) {
|
|
24
|
+
* const data = result.__interrupt__[0].value;
|
|
25
|
+
* const decision = await notifier.notify({
|
|
26
|
+
* title: data.question,
|
|
27
|
+
* customId: data.customId,
|
|
28
|
+
* timeoutMs: 600_000,
|
|
29
|
+
* });
|
|
30
|
+
* await graph.invoke(new Command({ resume: decision }), config);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Resolves `"approved"` or `"rejected"` and **never anything else** — a failure
|
|
35
|
+
* to reach anybody comes back as `"rejected"`, because a graph that resumes
|
|
36
|
+
* with an ambiguous value resumes with whatever the node's `if` happens to do
|
|
37
|
+
* with it.
|
|
38
|
+
*/
|
|
39
|
+
export class M91InterruptNotifier {
|
|
40
|
+
client;
|
|
41
|
+
defaults;
|
|
42
|
+
constructor(client, defaults = {}) {
|
|
43
|
+
this.client = client;
|
|
44
|
+
this.defaults = defaults;
|
|
45
|
+
}
|
|
46
|
+
async notify(options) {
|
|
47
|
+
const { title, customId, ...rest } = options;
|
|
48
|
+
const merged = { ...this.defaults, ...strip(rest) };
|
|
49
|
+
try {
|
|
50
|
+
const approved = await this.client.requestApproval(title, {
|
|
51
|
+
customId,
|
|
52
|
+
severity: 'HIGH',
|
|
53
|
+
timeoutMs: 300_000,
|
|
54
|
+
...merged,
|
|
55
|
+
});
|
|
56
|
+
return approved ? APPROVED : REJECTED;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Nobody could be reached, so nobody approved. Resuming a graph with
|
|
60
|
+
// "approved" because the pager was down is the one outcome worth ruling
|
|
61
|
+
// out by construction.
|
|
62
|
+
return REJECTED;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a graph node that asks a human and writes the answer into state.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* builder.addNode('humanApproval', createM91ApprovalNode(client, {
|
|
71
|
+
* approveLabel: 'Deploy Now',
|
|
72
|
+
* rejectLabel: 'Hold Release',
|
|
73
|
+
* timeoutMs: 600_000,
|
|
74
|
+
* }));
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* The node reads `title` (or `question`) and `customId` from state and returns
|
|
78
|
+
* `{ decision, approved }` for the next edge to branch on. Anything passed here
|
|
79
|
+
* is a default the state can override per run.
|
|
80
|
+
*/
|
|
81
|
+
export function createM91ApprovalNode(client, defaults = {}) {
|
|
82
|
+
const notifier = new M91InterruptNotifier(client, defaults);
|
|
83
|
+
return async (state) => {
|
|
84
|
+
const data = (state && typeof state === 'object' ? state : {});
|
|
85
|
+
const title = data.title ?? data.question;
|
|
86
|
+
const customId = data.customId ?? data.custom_id;
|
|
87
|
+
if (!title || !customId) {
|
|
88
|
+
// Said loudly rather than defaulted. A node that invents a customId is a
|
|
89
|
+
// node whose alerts never deduplicate and never close.
|
|
90
|
+
return {
|
|
91
|
+
decision: REJECTED,
|
|
92
|
+
approved: false,
|
|
93
|
+
error: "error: the approval node needs 'title' (or 'question') and 'customId' in state",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const decision = await notifier.notify({
|
|
97
|
+
title: String(title),
|
|
98
|
+
customId: String(customId),
|
|
99
|
+
...strip({
|
|
100
|
+
description: data.description,
|
|
101
|
+
severity: data.severity,
|
|
102
|
+
approveLabel: data.approveLabel,
|
|
103
|
+
rejectLabel: data.rejectLabel,
|
|
104
|
+
timeoutMs: data.timeoutMs,
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
return { decision, approved: decision === APPROVED };
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Drop undefined keys so a spread cannot blank out a default with `undefined`. */
|
|
111
|
+
function strip(value) {
|
|
112
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=langgraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"langgraph.js","sourceRoot":"","sources":["../../../src/integrations/langgraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiBhD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,oBAAoB;IACd,MAAM,CAAY;IAClB,QAAQ,CAAiB;IAE1C,YAAY,MAAiB,EAAE,WAA2B,EAAE;QAC1D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAsB;QACjC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;QAC7C,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE;gBACxD,QAAQ;gBACR,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,OAAO;gBAClB,GAAG,MAAM;aACV,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,wEAAwE;YACxE,uBAAuB;YACvB,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,WAA2B,EAAE;IAE7B,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE5D,OAAO,KAAK,EAAE,KAAc,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;QAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC;QAEjD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,yEAAyE;YACzE,uDAAuD;YACvD,OAAO;gBACL,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,KAAK;gBACf,KAAK,EACH,gFAAgF;aACnF,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;YACpB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;YAC1B,GAAG,KAAK,CAAC;gBACP,WAAW,EAAE,IAAI,CAAC,WAAiC;gBACnD,QAAQ,EAAE,IAAI,CAAC,QAAgC;gBAC/C,YAAY,EAAE,IAAI,CAAC,YAAkC;gBACrD,WAAW,EAAE,IAAI,CAAC,WAAiC;gBACnD,SAAS,EAAE,IAAI,CAAC,SAA+B;aAChD,CAAC;SACH,CAAC,CAAC;QACH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,SAAS,KAAK,CAAoC,KAAQ;IACxD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAe,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra (`@mastra/core`): M91 as the approver for `requireApproval` tools,
|
|
3
|
+
* and M91 as tools.
|
|
4
|
+
*
|
|
5
|
+
* **Approver.** Mark your risky tools `requireApproval: true`. Mastra suspends
|
|
6
|
+
* the run when the agent calls one; `generateWithM91Approvals` rings a phone
|
|
7
|
+
* for each suspended call and approves or declines it with the person's
|
|
8
|
+
* answer until the run finishes.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Agent } from '@mastra/core/agent';
|
|
12
|
+
* import { createTool } from '@mastra/core/tools';
|
|
13
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
14
|
+
* import { generateWithM91Approvals } from '@msg91comm/m91/integrations/mastra';
|
|
15
|
+
*
|
|
16
|
+
* const issueRefund = createTool({
|
|
17
|
+
* id: 'issue-refund',
|
|
18
|
+
* description: 'Refund an order.',
|
|
19
|
+
* inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
|
|
20
|
+
* requireApproval: true,
|
|
21
|
+
* execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* const agent = new Agent({ id: 'support', name: 'Support', instructions: '…',
|
|
25
|
+
* model: 'openai/gpt-5-mini', tools: { issueRefund } });
|
|
26
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
27
|
+
*
|
|
28
|
+
* const output = await generateWithM91Approvals(agent, 'Refund ORD-40122.', client);
|
|
29
|
+
* console.log(output.text);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Mastra keeps suspended runs in its storage, so configure a storage provider
|
|
33
|
+
* on your `Mastra` instance for approvals to resume.
|
|
34
|
+
*/
|
|
35
|
+
import type { Tool } from '@mastra/core/tools';
|
|
36
|
+
import type { M91Client } from '../client.js';
|
|
37
|
+
import { type AlertDefaults, type ApprovalDefaults, type GateOptions } from './specs.js';
|
|
38
|
+
/** The parts of a Mastra `generate()` result this file reads. */
|
|
39
|
+
export interface MastraOutputLike {
|
|
40
|
+
text: string;
|
|
41
|
+
finishReason?: unknown;
|
|
42
|
+
runId?: string;
|
|
43
|
+
suspendPayload?: unknown;
|
|
44
|
+
}
|
|
45
|
+
/** The parts of a Mastra `Agent` this file calls. */
|
|
46
|
+
export interface MastraAgentLike<TOutput extends MastraOutputLike = MastraOutputLike> {
|
|
47
|
+
generate(prompt: any, options?: any): Promise<TOutput>;
|
|
48
|
+
approveToolCallGenerate(options: {
|
|
49
|
+
runId: string;
|
|
50
|
+
toolCallId?: string;
|
|
51
|
+
}): Promise<TOutput>;
|
|
52
|
+
declineToolCallGenerate(options: {
|
|
53
|
+
runId: string;
|
|
54
|
+
toolCallId?: string;
|
|
55
|
+
reason?: string;
|
|
56
|
+
}): Promise<TOutput>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* `agent.generate()`, with every tool approval the run suspends for answered
|
|
60
|
+
* on a phone through M91. Approvals for tools outside `options.tools` are
|
|
61
|
+
* returned still suspended, for your own code.
|
|
62
|
+
*/
|
|
63
|
+
export declare function generateWithM91Approvals<TOutput extends MastraOutputLike>(agent: MastraAgentLike<TOutput>, prompt: unknown, client: M91Client, options?: GateOptions & {
|
|
64
|
+
generateOptions?: Record<string, unknown>;
|
|
65
|
+
maxRounds?: number;
|
|
66
|
+
}): Promise<TOutput>;
|
|
67
|
+
/** A Mastra tool, typed loosely enough to drop into any agent's `tools`. */
|
|
68
|
+
export type M91MastraTool = Tool<any, any, any, any, any, string, any>;
|
|
69
|
+
/** The blocking approval tool, `m91_request_approval`, as a Mastra tool. */
|
|
70
|
+
export declare function createM91ApprovalTool(client: M91Client, defaults?: ApprovalDefaults): Promise<M91MastraTool>;
|
|
71
|
+
/** The fire-and-forget alert tool, `m91_send_alert`, as a Mastra tool. */
|
|
72
|
+
export declare function createM91AlertTool(client: M91Client, defaults?: AlertDefaults): Promise<M91MastraTool>;
|
|
73
|
+
/** Both tools, keyed for an `Agent`'s `tools` object. */
|
|
74
|
+
export declare function createM91Tools(client: M91Client, defaults?: ApprovalDefaults & AlertDefaults): Promise<Record<string, M91MastraTool>>;
|
|
75
|
+
//# sourceMappingURL=mastra.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mastra.d.ts","sourceRoot":"","sources":["../../../src/integrations/mastra.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAML,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAC;AAGpB,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB;IAClF,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,uBAAuB,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,uBAAuB,CAAC,OAAO,EAAE;QAC/B,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAeD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,SAAS,gBAAgB,EAC7E,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAC/B,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,WAAW,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5F,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAiBD,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;AAEvE,4EAA4E;AAC5E,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAqB,GAC9B,OAAO,CAAC,aAAa,CAAC,CASxB;AAED,0EAA0E;AAC1E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,aAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,CASxB;AAED,yDAAyD;AACzD,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAgB,GAAG,aAAkB,GAC9C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAKxC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra (`@mastra/core`): M91 as the approver for `requireApproval` tools,
|
|
3
|
+
* and M91 as tools.
|
|
4
|
+
*
|
|
5
|
+
* **Approver.** Mark your risky tools `requireApproval: true`. Mastra suspends
|
|
6
|
+
* the run when the agent calls one; `generateWithM91Approvals` rings a phone
|
|
7
|
+
* for each suspended call and approves or declines it with the person's
|
|
8
|
+
* answer until the run finishes.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Agent } from '@mastra/core/agent';
|
|
12
|
+
* import { createTool } from '@mastra/core/tools';
|
|
13
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
14
|
+
* import { generateWithM91Approvals } from '@msg91comm/m91/integrations/mastra';
|
|
15
|
+
*
|
|
16
|
+
* const issueRefund = createTool({
|
|
17
|
+
* id: 'issue-refund',
|
|
18
|
+
* description: 'Refund an order.',
|
|
19
|
+
* inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
|
|
20
|
+
* requireApproval: true,
|
|
21
|
+
* execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* const agent = new Agent({ id: 'support', name: 'Support', instructions: '…',
|
|
25
|
+
* model: 'openai/gpt-5-mini', tools: { issueRefund } });
|
|
26
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
27
|
+
*
|
|
28
|
+
* const output = await generateWithM91Approvals(agent, 'Refund ORD-40122.', client);
|
|
29
|
+
* console.log(output.text);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Mastra keeps suspended runs in its storage, so configure a storage provider
|
|
33
|
+
* on your `Mastra` instance for approvals to resume.
|
|
34
|
+
*/
|
|
35
|
+
import { ALERT_DESCRIPTION, ALERT_TOOL_NAME, APPROVAL_DESCRIPTION, APPROVAL_TOOL_NAME, decideToolCall, needsHuman, runAlert, runApproval, } from './specs.js';
|
|
36
|
+
import { alertShape, approvalShape, loadZod } from './zod-shapes.js';
|
|
37
|
+
function pendingApproval(output) {
|
|
38
|
+
if (output.finishReason !== 'suspended' || !output.runId)
|
|
39
|
+
return null;
|
|
40
|
+
const payload = output.suspendPayload;
|
|
41
|
+
if (!payload?.toolCallId || !payload.toolName)
|
|
42
|
+
return null;
|
|
43
|
+
return { toolCallId: payload.toolCallId, toolName: payload.toolName, args: payload.args };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `agent.generate()`, with every tool approval the run suspends for answered
|
|
47
|
+
* on a phone through M91. Approvals for tools outside `options.tools` are
|
|
48
|
+
* returned still suspended, for your own code.
|
|
49
|
+
*/
|
|
50
|
+
export async function generateWithM91Approvals(agent, prompt, client, options = {}) {
|
|
51
|
+
const { generateOptions, maxRounds = 20, ...gate } = options;
|
|
52
|
+
let output = await agent.generate(prompt, generateOptions);
|
|
53
|
+
for (let round = 0; round < maxRounds; round += 1) {
|
|
54
|
+
const pending = pendingApproval(output);
|
|
55
|
+
if (!pending || !(await needsHuman(gate, pending.toolName, pending.args)))
|
|
56
|
+
break;
|
|
57
|
+
const runId = output.runId;
|
|
58
|
+
const decision = await decideToolCall(client, { toolName: pending.toolName, input: pending.args, callId: pending.toolCallId }, gate);
|
|
59
|
+
output = decision.approved
|
|
60
|
+
? await agent.approveToolCallGenerate({ runId, toolCallId: pending.toolCallId })
|
|
61
|
+
: await agent.declineToolCallGenerate({
|
|
62
|
+
runId,
|
|
63
|
+
toolCallId: pending.toolCallId,
|
|
64
|
+
reason: decision.reason,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return output;
|
|
68
|
+
}
|
|
69
|
+
async function mastraTools() {
|
|
70
|
+
try {
|
|
71
|
+
const specifier = '@mastra/core/tools';
|
|
72
|
+
return (await import(specifier));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new Error('The Mastra integration needs @mastra/core. Install it with: npm install @mastra/core', {
|
|
76
|
+
cause: error,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The blocking approval tool, `m91_request_approval`, as a Mastra tool. */
|
|
81
|
+
export async function createM91ApprovalTool(client, defaults = {}) {
|
|
82
|
+
const { createTool } = await mastraTools();
|
|
83
|
+
const z = await loadZod('Mastra');
|
|
84
|
+
return createTool({
|
|
85
|
+
id: APPROVAL_TOOL_NAME,
|
|
86
|
+
description: APPROVAL_DESCRIPTION,
|
|
87
|
+
inputSchema: z.object(approvalShape(z)),
|
|
88
|
+
execute: async (args) => ({ result: await runApproval(client, args, defaults) }),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** The fire-and-forget alert tool, `m91_send_alert`, as a Mastra tool. */
|
|
92
|
+
export async function createM91AlertTool(client, defaults = {}) {
|
|
93
|
+
const { createTool } = await mastraTools();
|
|
94
|
+
const z = await loadZod('Mastra');
|
|
95
|
+
return createTool({
|
|
96
|
+
id: ALERT_TOOL_NAME,
|
|
97
|
+
description: ALERT_DESCRIPTION,
|
|
98
|
+
inputSchema: z.object(alertShape(z)),
|
|
99
|
+
execute: async (args) => ({ result: await runAlert(client, args, defaults) }),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Both tools, keyed for an `Agent`'s `tools` object. */
|
|
103
|
+
export async function createM91Tools(client, defaults = {}) {
|
|
104
|
+
return {
|
|
105
|
+
[APPROVAL_TOOL_NAME]: await createM91ApprovalTool(client, defaults),
|
|
106
|
+
[ALERT_TOOL_NAME]: await createM91AlertTool(client, defaults),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=mastra.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mastra.js","sourceRoot":"","sources":["../../../src/integrations/mastra.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAMlB,cAAc,EACd,UAAU,EACV,QAAQ,EACR,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AA2BrE,SAAS,eAAe,CAAC,MAAwB;IAC/C,IAAI,MAAM,CAAC,YAAY,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACtE,MAAM,OAAO,GAAG,MAAM,CAAC,cAAsD,CAAC;IAC9E,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3D,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAC5F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAA+B,EAC/B,MAAe,EACf,MAAiB,EACjB,UAA2F,EAAE;IAE7F,MAAM,EAAE,eAAe,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAC7D,IAAI,MAAM,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YAAE,MAAM;QACjF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAe,CAAC;QACrC,MAAM,QAAQ,GAAG,MAAM,cAAc,CACnC,MAAM,EACN,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,EAC/E,IAAI,CACL,CAAC;QACF,MAAM,GAAG,QAAQ,CAAC,QAAQ;YACxB,CAAC,CAAC,MAAM,KAAK,CAAC,uBAAuB,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;YAChF,CAAC,CAAC,MAAM,KAAK,CAAC,uBAAuB,CAAC;gBAClC,KAAK;gBACL,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB,CAAC,CAAC;IACT,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,oBAAoB,CAAC;QACvC,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAiC,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,sFAAsF,EAAE;YACtG,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAKD,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAiB,EACjB,WAA6B,EAAE;IAE/B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,WAAW,EAAE,CAAC;IAC3C,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,UAAU,CAAC;QAChB,EAAE,EAAE,kBAAkB;QACtB,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACvC,OAAO,EAAE,KAAK,EAAE,IAAkB,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;KAC/F,CAAkB,CAAC;AACtB,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAiB,EACjB,WAA0B,EAAE;IAE5B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,WAAW,EAAE,CAAC;IAC3C,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,UAAU,CAAC;QAChB,EAAE,EAAE,eAAe;QACnB,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO,EAAE,KAAK,EAAE,IAAe,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;KACzF,CAAkB,CAAC;AACtB,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAiB,EACjB,WAA6C,EAAE;IAE/C,OAAO;QACL,CAAC,kBAAkB,CAAC,EAAE,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC;QACnE,CAAC,eAAe,CAAC,EAAE,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC;KAC9D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Agents SDK (`@openai/agents`): two tools, and M91 as the approver.
|
|
3
|
+
*
|
|
4
|
+
* **Approver.** Mark your risky tools `needsApproval: true`. The SDK pauses the
|
|
5
|
+
* run and returns `result.interruptions`; `runWithM91Approvals` rings a phone
|
|
6
|
+
* for each one, approves or rejects it on the run state with the person's
|
|
7
|
+
* answer, and resumes the same run until it finishes.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Agent, tool } from '@openai/agents';
|
|
11
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
12
|
+
* import { runWithM91Approvals } from '@msg91comm/m91/integrations/openai-agents';
|
|
13
|
+
*
|
|
14
|
+
* const issueRefund = tool({
|
|
15
|
+
* name: 'issue_refund',
|
|
16
|
+
* description: 'Refund an order.',
|
|
17
|
+
* parameters: z.object({ orderId: z.string(), amount: z.number() }),
|
|
18
|
+
* needsApproval: true,
|
|
19
|
+
* execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* const agent = new Agent({ name: 'Support', instructions: '…', tools: [issueRefund] });
|
|
23
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
24
|
+
*
|
|
25
|
+
* const result = await runWithM91Approvals(agent, 'Refund ORD-40122, it never arrived.', client);
|
|
26
|
+
* console.log(result.finalOutput);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* **Tools** (`createM91Tools`) are for when the model itself should decide to
|
|
30
|
+
* ask, or to wake somebody about something that happened.
|
|
31
|
+
*
|
|
32
|
+
* `@openai/agents` is an optional peer dependency, imported only when one of
|
|
33
|
+
* these is called.
|
|
34
|
+
*/
|
|
35
|
+
import type { Agent, FunctionTool, RunResult } from '@openai/agents';
|
|
36
|
+
import type { M91Client } from '../client.js';
|
|
37
|
+
import { type AlertDefaults, type ApprovalDefaults, type GateOptions } from './specs.js';
|
|
38
|
+
/** The blocking approval tool, `m91_request_approval`. Returns `"approved"`, `"rejected"` or `"error: …"`. */
|
|
39
|
+
export declare function createM91ApprovalTool(client: M91Client, defaults?: ApprovalDefaults): Promise<FunctionTool>;
|
|
40
|
+
/** The fire-and-forget alert tool, `m91_send_alert`. Returns `"sent:delivered=N"` or `"error: …"`. */
|
|
41
|
+
export declare function createM91AlertTool(client: M91Client, defaults?: AlertDefaults): Promise<FunctionTool>;
|
|
42
|
+
/** Both tools, for an agent's `tools` array. */
|
|
43
|
+
export declare function createM91Tools(client: M91Client, defaults?: ApprovalDefaults & AlertDefaults): Promise<FunctionTool[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Ask a person about every pending approval on a paused run, and record the
|
|
46
|
+
* answers on its state. Returns how many were decided; resume with
|
|
47
|
+
* `run(agent, result.state)`.
|
|
48
|
+
*
|
|
49
|
+
* Interruptions for tools outside `options.tools` are left pending for your
|
|
50
|
+
* own code to decide.
|
|
51
|
+
*/
|
|
52
|
+
export declare function approveInterruptionsWithM91(result: Pick<RunResult<any, any>, 'interruptions' | 'state'>, client: M91Client, options?: GateOptions): Promise<number>;
|
|
53
|
+
/**
|
|
54
|
+
* `run()`, with every approval the run pauses for answered on a phone through
|
|
55
|
+
* M91, until the run finishes or nothing left to decide is M91's.
|
|
56
|
+
*/
|
|
57
|
+
export declare function runWithM91Approvals<TAgent extends Agent<any, any>>(agent: TAgent, input: string | Parameters<typeof import('@openai/agents').run>[1], client: M91Client, options?: GateOptions & {
|
|
58
|
+
runOptions?: Record<string, unknown>;
|
|
59
|
+
maxRounds?: number;
|
|
60
|
+
}): Promise<RunResult<any, TAgent>>;
|
|
61
|
+
//# sourceMappingURL=openai-agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-agents.d.ts","sourceRoot":"","sources":["../../../src/integrations/openai-agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAiC,MAAM,gBAAgB,CAAC;AAEpG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAQL,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAC;AAmBpB,8GAA8G;AAC9G,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAqB,GAC9B,OAAO,CAAC,YAAY,CAAC,CAUvB;AAED,sGAAsG;AACtG,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,aAAkB,GAC3B,OAAO,CAAC,YAAY,CAAC,CASvB;AAED,gDAAgD;AAChD,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAgB,GAAG,aAAkB,GAC9C,OAAO,CAAC,YAAY,EAAE,CAAC,CAEzB;AAiBD;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,EAC5D,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,EACtE,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,cAAc,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAClE,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACvF,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CASjC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Agents SDK (`@openai/agents`): two tools, and M91 as the approver.
|
|
3
|
+
*
|
|
4
|
+
* **Approver.** Mark your risky tools `needsApproval: true`. The SDK pauses the
|
|
5
|
+
* run and returns `result.interruptions`; `runWithM91Approvals` rings a phone
|
|
6
|
+
* for each one, approves or rejects it on the run state with the person's
|
|
7
|
+
* answer, and resumes the same run until it finishes.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Agent, tool } from '@openai/agents';
|
|
11
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
12
|
+
* import { runWithM91Approvals } from '@msg91comm/m91/integrations/openai-agents';
|
|
13
|
+
*
|
|
14
|
+
* const issueRefund = tool({
|
|
15
|
+
* name: 'issue_refund',
|
|
16
|
+
* description: 'Refund an order.',
|
|
17
|
+
* parameters: z.object({ orderId: z.string(), amount: z.number() }),
|
|
18
|
+
* needsApproval: true,
|
|
19
|
+
* execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* const agent = new Agent({ name: 'Support', instructions: '…', tools: [issueRefund] });
|
|
23
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
24
|
+
*
|
|
25
|
+
* const result = await runWithM91Approvals(agent, 'Refund ORD-40122, it never arrived.', client);
|
|
26
|
+
* console.log(result.finalOutput);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* **Tools** (`createM91Tools`) are for when the model itself should decide to
|
|
30
|
+
* ask, or to wake somebody about something that happened.
|
|
31
|
+
*
|
|
32
|
+
* `@openai/agents` is an optional peer dependency, imported only when one of
|
|
33
|
+
* these is called.
|
|
34
|
+
*/
|
|
35
|
+
import { ALERT_DESCRIPTION, ALERT_SCHEMA, ALERT_TOOL_NAME, APPROVAL_DESCRIPTION, APPROVAL_SCHEMA, APPROVAL_TOOL_NAME, decideToolCall, needsHuman, runAlert, runApproval, } from './specs.js';
|
|
36
|
+
async function agentsModule() {
|
|
37
|
+
try {
|
|
38
|
+
const specifier = '@openai/agents';
|
|
39
|
+
return (await import(specifier));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error('The OpenAI Agents integration needs @openai/agents. Install it with: npm install @openai/agents', { cause: error });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** The blocking approval tool, `m91_request_approval`. Returns `"approved"`, `"rejected"` or `"error: …"`. */
|
|
46
|
+
export async function createM91ApprovalTool(client, defaults = {}) {
|
|
47
|
+
const { tool } = await agentsModule();
|
|
48
|
+
return tool({
|
|
49
|
+
name: APPROVAL_TOOL_NAME,
|
|
50
|
+
description: APPROVAL_DESCRIPTION,
|
|
51
|
+
// JSON Schema with optional fields: the non-strict mode accepts it as is.
|
|
52
|
+
parameters: APPROVAL_SCHEMA,
|
|
53
|
+
strict: false,
|
|
54
|
+
execute: (args) => runApproval(client, args, defaults),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** The fire-and-forget alert tool, `m91_send_alert`. Returns `"sent:delivered=N"` or `"error: …"`. */
|
|
58
|
+
export async function createM91AlertTool(client, defaults = {}) {
|
|
59
|
+
const { tool } = await agentsModule();
|
|
60
|
+
return tool({
|
|
61
|
+
name: ALERT_TOOL_NAME,
|
|
62
|
+
description: ALERT_DESCRIPTION,
|
|
63
|
+
parameters: ALERT_SCHEMA,
|
|
64
|
+
strict: false,
|
|
65
|
+
execute: (args) => runAlert(client, args, defaults),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Both tools, for an agent's `tools` array. */
|
|
69
|
+
export async function createM91Tools(client, defaults = {}) {
|
|
70
|
+
return [await createM91ApprovalTool(client, defaults), await createM91AlertTool(client, defaults)];
|
|
71
|
+
}
|
|
72
|
+
function parseArguments(item) {
|
|
73
|
+
const raw = item.arguments;
|
|
74
|
+
if (raw === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(raw);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return raw;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function callIdOf(item) {
|
|
84
|
+
const raw = item.rawItem;
|
|
85
|
+
return raw.callId ?? raw.id ?? `${item.name ?? 'tool'}-${item.arguments ?? ''}`.slice(0, 60);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Ask a person about every pending approval on a paused run, and record the
|
|
89
|
+
* answers on its state. Returns how many were decided; resume with
|
|
90
|
+
* `run(agent, result.state)`.
|
|
91
|
+
*
|
|
92
|
+
* Interruptions for tools outside `options.tools` are left pending for your
|
|
93
|
+
* own code to decide.
|
|
94
|
+
*/
|
|
95
|
+
export async function approveInterruptionsWithM91(result, client, options = {}) {
|
|
96
|
+
const state = result.state;
|
|
97
|
+
let decided = 0;
|
|
98
|
+
for (const item of result.interruptions ?? []) {
|
|
99
|
+
const toolName = item.name ?? 'tool';
|
|
100
|
+
const input = parseArguments(item);
|
|
101
|
+
if (!(await needsHuman(options, toolName, input)))
|
|
102
|
+
continue;
|
|
103
|
+
const decision = await decideToolCall(client, { toolName, input, callId: callIdOf(item) }, options);
|
|
104
|
+
if (decision.approved)
|
|
105
|
+
state.approve(item);
|
|
106
|
+
else
|
|
107
|
+
state.reject(item, { message: decision.reason });
|
|
108
|
+
decided += 1;
|
|
109
|
+
}
|
|
110
|
+
return decided;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `run()`, with every approval the run pauses for answered on a phone through
|
|
114
|
+
* M91, until the run finishes or nothing left to decide is M91's.
|
|
115
|
+
*/
|
|
116
|
+
export async function runWithM91Approvals(agent, input, client, options = {}) {
|
|
117
|
+
const { run } = await agentsModule();
|
|
118
|
+
const { runOptions, maxRounds = 20, ...gate } = options;
|
|
119
|
+
let result = (await run(agent, input, runOptions));
|
|
120
|
+
for (let round = 0; round < maxRounds && (result.interruptions?.length ?? 0) > 0; round += 1) {
|
|
121
|
+
if ((await approveInterruptionsWithM91(result, client, gate)) === 0)
|
|
122
|
+
break;
|
|
123
|
+
result = (await run(agent, result.state, runOptions));
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=openai-agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-agents.js","sourceRoot":"","sources":["../../../src/integrations/openai-agents.ts"],"names":[],"mappings":"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"}
|