@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,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tool definitions, written once and adapted per framework.
|
|
3
|
+
*
|
|
4
|
+
* A tool's name, its description and its argument docs are the entire interface
|
|
5
|
+
* a model has to M91 — they are the prompt. Four adapters each spelling their
|
|
6
|
+
* own copy is four descriptions that drift, and the drift is invisible until an
|
|
7
|
+
* agent on one framework behaves differently from the same agent on another.
|
|
8
|
+
*
|
|
9
|
+
* Every adapter therefore imports the schema and the `run` from here and does
|
|
10
|
+
* nothing but wrap it in the shape its framework expects.
|
|
11
|
+
*/
|
|
12
|
+
import type { M91Client } from '../client.js';
|
|
13
|
+
import type { AlertResponse, Severity } from '../models.js';
|
|
14
|
+
export declare const APPROVED = "approved";
|
|
15
|
+
export declare const REJECTED = "rejected";
|
|
16
|
+
/**
|
|
17
|
+
* `error: …` — never `approved`, whatever went wrong.
|
|
18
|
+
*
|
|
19
|
+
* The message is redacted: an M91 error can carry the URL it failed on, and
|
|
20
|
+
* that URL has the send token in its path. A tool result goes straight into a
|
|
21
|
+
* model's context and from there into logs, traces and evals.
|
|
22
|
+
*/
|
|
23
|
+
export declare function errorResult(error: unknown): string;
|
|
24
|
+
/**
|
|
25
|
+
* `sent:delivered=N` — plus a word when nobody was actually reached.
|
|
26
|
+
*
|
|
27
|
+
* A model that reads "sent" and stops has no way to know the channel's phones
|
|
28
|
+
* were all stale. Saying so in the result is the only channel back to it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function alertResult(alert: AlertResponse): string;
|
|
31
|
+
/** JSON Schema rather than zod, so nothing here needs a dependency to exist. */
|
|
32
|
+
export declare const APPROVAL_SCHEMA: {
|
|
33
|
+
readonly type: "object";
|
|
34
|
+
readonly properties: {
|
|
35
|
+
readonly title: {
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
readonly description: string;
|
|
38
|
+
};
|
|
39
|
+
readonly customId: {
|
|
40
|
+
readonly type: "string";
|
|
41
|
+
readonly description: string;
|
|
42
|
+
};
|
|
43
|
+
readonly description: {
|
|
44
|
+
readonly type: "string";
|
|
45
|
+
readonly description: string;
|
|
46
|
+
};
|
|
47
|
+
readonly severity: {
|
|
48
|
+
readonly type: "string";
|
|
49
|
+
readonly enum: readonly ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
50
|
+
readonly description: "LOW, MEDIUM, HIGH or CRITICAL. All of them ring through Do Not Disturb; HIGH and CRITICAL also place a voice call after 45 seconds of silence. An approval that blocks you is normally HIGH.";
|
|
51
|
+
};
|
|
52
|
+
readonly approveLabel: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly description: "The yes button, 24 characters or fewer.";
|
|
55
|
+
};
|
|
56
|
+
readonly rejectLabel: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly description: "The no button, 24 characters or fewer.";
|
|
59
|
+
};
|
|
60
|
+
readonly timeoutSeconds: {
|
|
61
|
+
readonly type: "number";
|
|
62
|
+
readonly description: "How long to wait for an answer before giving up. Default 300.";
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
readonly required: readonly ["title", "customId"];
|
|
66
|
+
readonly additionalProperties: false;
|
|
67
|
+
};
|
|
68
|
+
export declare const ALERT_SCHEMA: {
|
|
69
|
+
readonly type: "object";
|
|
70
|
+
readonly properties: {
|
|
71
|
+
readonly title: {
|
|
72
|
+
readonly type: "string";
|
|
73
|
+
readonly description: "What happened, in one line somebody can act on. 5-2000 characters.";
|
|
74
|
+
};
|
|
75
|
+
readonly customId: {
|
|
76
|
+
readonly type: "string";
|
|
77
|
+
readonly description: string;
|
|
78
|
+
};
|
|
79
|
+
readonly description: {
|
|
80
|
+
readonly type: "string";
|
|
81
|
+
readonly description: "Any detail that helps them act.";
|
|
82
|
+
};
|
|
83
|
+
readonly severity: {
|
|
84
|
+
readonly type: "string";
|
|
85
|
+
readonly enum: readonly ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
86
|
+
readonly description: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
readonly required: readonly ["title"];
|
|
90
|
+
readonly additionalProperties: false;
|
|
91
|
+
};
|
|
92
|
+
export declare const APPROVAL_TOOL_NAME = "m91_request_approval";
|
|
93
|
+
export declare const ALERT_TOOL_NAME = "m91_send_alert";
|
|
94
|
+
export declare const APPROVAL_DESCRIPTION: string;
|
|
95
|
+
export declare const ALERT_DESCRIPTION: string;
|
|
96
|
+
export interface ApprovalArgs {
|
|
97
|
+
title: string;
|
|
98
|
+
customId: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
severity?: Severity;
|
|
101
|
+
approveLabel?: string;
|
|
102
|
+
rejectLabel?: string;
|
|
103
|
+
timeoutSeconds?: number;
|
|
104
|
+
}
|
|
105
|
+
export interface AlertArgs {
|
|
106
|
+
title: string;
|
|
107
|
+
customId?: string;
|
|
108
|
+
description?: string;
|
|
109
|
+
severity?: Severity;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* What a deployment wants when the model does not say.
|
|
113
|
+
*
|
|
114
|
+
* Set these where the tool is built, not in the prompt. How long a person has
|
|
115
|
+
* to wake up, and what the buttons say, are operations decisions — a model
|
|
116
|
+
* choosing its own timeout is a model deciding how long somebody has to answer.
|
|
117
|
+
* It can still override per call when it has a reason.
|
|
118
|
+
*/
|
|
119
|
+
export interface ApprovalDefaults {
|
|
120
|
+
severity?: Severity;
|
|
121
|
+
approveLabel?: string;
|
|
122
|
+
rejectLabel?: string;
|
|
123
|
+
timeoutSeconds?: number;
|
|
124
|
+
}
|
|
125
|
+
export interface AlertDefaults {
|
|
126
|
+
severity?: Severity;
|
|
127
|
+
}
|
|
128
|
+
/** Ask, wait, and answer in a word a model cannot misread. */
|
|
129
|
+
export declare function runApproval(client: M91Client, args: ApprovalArgs, defaults?: ApprovalDefaults): Promise<string>;
|
|
130
|
+
export declare function runAlert(client: M91Client, args: AlertArgs, defaults?: AlertDefaults): Promise<string>;
|
|
131
|
+
/**
|
|
132
|
+
* One tool call a framework is about to make, in the only three fields every
|
|
133
|
+
* framework agrees on.
|
|
134
|
+
*/
|
|
135
|
+
export interface GatedCall {
|
|
136
|
+
toolName: string;
|
|
137
|
+
input: unknown;
|
|
138
|
+
/** The framework's id for this call. Becomes the alert's customId. */
|
|
139
|
+
callId: string;
|
|
140
|
+
}
|
|
141
|
+
/** How a gate decides who gets asked, and how the question reads. */
|
|
142
|
+
export interface GateOptions extends ApprovalDefaults {
|
|
143
|
+
/**
|
|
144
|
+
* Which tool calls need a human: a list of tool names, or a predicate.
|
|
145
|
+
*
|
|
146
|
+
* Required where a framework routes every call through the gate (Claude
|
|
147
|
+
* Agent SDK, Google ADK, Vercel `toolApproval`): ringing somebody's phone
|
|
148
|
+
* for every file read is how a channel gets muted.
|
|
149
|
+
*/
|
|
150
|
+
tools?: readonly string[] | ((toolName: string, input: unknown) => boolean | Promise<boolean>);
|
|
151
|
+
/** Write the lock-screen question yourself. Default: "Allow <tool>?" plus the arguments. */
|
|
152
|
+
describe?: (call: GatedCall) => {
|
|
153
|
+
title: string;
|
|
154
|
+
description?: string;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export interface GateDecision {
|
|
158
|
+
approved: boolean;
|
|
159
|
+
/** Why, in words a model can repeat to the user. */
|
|
160
|
+
reason: string;
|
|
161
|
+
}
|
|
162
|
+
/** Does this call need a human? With no `tools`, every call that reaches the gate does. */
|
|
163
|
+
export declare function needsHuman(options: GateOptions, toolName: string, input: unknown): Promise<boolean>;
|
|
164
|
+
/** A customId M91 accepts, stable across a framework's retries of the same call. */
|
|
165
|
+
export declare function gateCustomId(call: GatedCall): string;
|
|
166
|
+
/**
|
|
167
|
+
* Ring a phone about a tool call and wait for the answer.
|
|
168
|
+
*
|
|
169
|
+
* Fails closed: an M91 error, an unreachable channel or a timeout is a no,
|
|
170
|
+
* with the reason spelled out, never a silent yes.
|
|
171
|
+
*/
|
|
172
|
+
export declare function decideToolCall(client: M91Client, call: GatedCall, options?: GateOptions): Promise<GateDecision>;
|
|
173
|
+
//# sourceMappingURL=specs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../../src/integrations/specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tool definitions, written once and adapted per framework.
|
|
3
|
+
*
|
|
4
|
+
* A tool's name, its description and its argument docs are the entire interface
|
|
5
|
+
* a model has to M91 — they are the prompt. Four adapters each spelling their
|
|
6
|
+
* own copy is four descriptions that drift, and the drift is invisible until an
|
|
7
|
+
* agent on one framework behaves differently from the same agent on another.
|
|
8
|
+
*
|
|
9
|
+
* Every adapter therefore imports the schema and the `run` from here and does
|
|
10
|
+
* nothing but wrap it in the shape its framework expects.
|
|
11
|
+
*/
|
|
12
|
+
import { M91Error } from '../exceptions.js';
|
|
13
|
+
import { redact } from '../http.js';
|
|
14
|
+
export const APPROVED = 'approved';
|
|
15
|
+
export const REJECTED = 'rejected';
|
|
16
|
+
/**
|
|
17
|
+
* `error: …` — never `approved`, whatever went wrong.
|
|
18
|
+
*
|
|
19
|
+
* The message is redacted: an M91 error can carry the URL it failed on, and
|
|
20
|
+
* that URL has the send token in its path. A tool result goes straight into a
|
|
21
|
+
* model's context and from there into logs, traces and evals.
|
|
22
|
+
*/
|
|
23
|
+
export function errorResult(error) {
|
|
24
|
+
if (error instanceof M91Error)
|
|
25
|
+
return `error: [${error.code}] ${redact(error.message)}`;
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
return `error: ${redact(message) || 'unknown failure'}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `sent:delivered=N` — plus a word when nobody was actually reached.
|
|
31
|
+
*
|
|
32
|
+
* A model that reads "sent" and stops has no way to know the channel's phones
|
|
33
|
+
* were all stale. Saying so in the result is the only channel back to it.
|
|
34
|
+
*/
|
|
35
|
+
export function alertResult(alert) {
|
|
36
|
+
if (alert.deduplicated) {
|
|
37
|
+
return ('sent:delivered=0 (an alert with this customId is already open and already ringing — ' +
|
|
38
|
+
'nobody was disturbed a second time)');
|
|
39
|
+
}
|
|
40
|
+
if (alert.delivered === 0) {
|
|
41
|
+
return (`sent:delivered=0 WARNING: the alert was filed but no phone could be reached ` +
|
|
42
|
+
`(unregistered=${alert.unregistered.length}, noDevice=${alert.noDevice}, ` +
|
|
43
|
+
`pending=${alert.pending}). Nobody has been woken.`);
|
|
44
|
+
}
|
|
45
|
+
return `sent:delivered=${alert.delivered}`;
|
|
46
|
+
}
|
|
47
|
+
const SEVERITY_DOC = 'LOW, MEDIUM, HIGH or CRITICAL. All of them ring through Do Not Disturb; HIGH and ' +
|
|
48
|
+
'CRITICAL also place a voice call after 45 seconds of silence.';
|
|
49
|
+
const CUSTOM_ID_DOC = 'A stable id for this exact request, derived from what is being decided ' +
|
|
50
|
+
"('refund-ORD-40122'). NEVER a timestamp, a UUID or a counter: the id is what " +
|
|
51
|
+
'deduplicates a retry and what closes the alert afterwards.';
|
|
52
|
+
/** JSON Schema rather than zod, so nothing here needs a dependency to exist. */
|
|
53
|
+
export const APPROVAL_SCHEMA = {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
title: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'The question, as the person reads it at 3am on a lock screen. Name the action ' +
|
|
59
|
+
"and its consequence: 'Refund 48,000 on ORD-40122?' — not 'approve action'. " +
|
|
60
|
+
'5-2000 characters.',
|
|
61
|
+
},
|
|
62
|
+
customId: { type: 'string', description: CUSTOM_ID_DOC },
|
|
63
|
+
description: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'The context the person needs to answer without asking you anything back: what ' +
|
|
66
|
+
'you found, what you are about to do, and what happens if they say no.',
|
|
67
|
+
},
|
|
68
|
+
severity: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
enum: ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'],
|
|
71
|
+
description: `${SEVERITY_DOC} An approval that blocks you is normally HIGH.`,
|
|
72
|
+
},
|
|
73
|
+
approveLabel: { type: 'string', description: 'The yes button, 24 characters or fewer.' },
|
|
74
|
+
rejectLabel: { type: 'string', description: 'The no button, 24 characters or fewer.' },
|
|
75
|
+
timeoutSeconds: {
|
|
76
|
+
type: 'number',
|
|
77
|
+
description: 'How long to wait for an answer before giving up. Default 300.',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ['title', 'customId'],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
};
|
|
83
|
+
export const ALERT_SCHEMA = {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
title: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'What happened, in one line somebody can act on. 5-2000 characters.',
|
|
89
|
+
},
|
|
90
|
+
customId: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: "A stable id for the CONDITION, not this occurrence — 'disk-full-node7'. Repeats " +
|
|
93
|
+
'while it is still open wake nobody a second time. Never a timestamp.',
|
|
94
|
+
},
|
|
95
|
+
description: { type: 'string', description: 'Any detail that helps them act.' },
|
|
96
|
+
severity: { type: 'string', enum: ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'], description: SEVERITY_DOC },
|
|
97
|
+
},
|
|
98
|
+
required: ['title'],
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
};
|
|
101
|
+
export const APPROVAL_TOOL_NAME = 'm91_request_approval';
|
|
102
|
+
export const ALERT_TOOL_NAME = 'm91_send_alert';
|
|
103
|
+
export const APPROVAL_DESCRIPTION = 'Ask a human for permission and wait for their answer. Use this BEFORE any ' +
|
|
104
|
+
'irreversible or consequential action: payments, refunds, deletions, deployments, ' +
|
|
105
|
+
"sending messages on someone's behalf, changing production data, or anything you " +
|
|
106
|
+
'would want a colleague to sign off on. ' +
|
|
107
|
+
'The person is woken on their phone immediately at full volume, through silent mode ' +
|
|
108
|
+
'and Do Not Disturb, and called if they do not answer within 45 seconds. ' +
|
|
109
|
+
'This call BLOCKS until they respond or the timeout passes. ' +
|
|
110
|
+
"Returns 'approved' or 'rejected'. Treat anything that is not exactly 'approved' as " +
|
|
111
|
+
'a refusal and do not perform the action.';
|
|
112
|
+
export const ALERT_DESCRIPTION = 'Wake a human immediately about something that has already happened, without waiting ' +
|
|
113
|
+
'for a reply. Use for failures, anomalies, breaches, and long jobs finishing that ' +
|
|
114
|
+
'somebody is waiting on. ' +
|
|
115
|
+
'The alert rings every phone on the on-call channel at full volume through silent ' +
|
|
116
|
+
'mode and Do Not Disturb, and keeps ringing until somebody answers. ' +
|
|
117
|
+
'Do NOT use this to ask permission — use m91_request_approval, which waits. ' +
|
|
118
|
+
'Do NOT use it for anything that could wait until morning; this wakes people up. ' +
|
|
119
|
+
"Returns 'sent:delivered=N', where N is how many phones were actually reached.";
|
|
120
|
+
/** Ask, wait, and answer in a word a model cannot misread. */
|
|
121
|
+
export async function runApproval(client, args, defaults = {}) {
|
|
122
|
+
try {
|
|
123
|
+
const approved = await client.requestApproval(args.title, {
|
|
124
|
+
customId: args.customId,
|
|
125
|
+
description: args.description,
|
|
126
|
+
severity: args.severity ?? defaults.severity ?? 'HIGH',
|
|
127
|
+
approveLabel: args.approveLabel ?? defaults.approveLabel ?? 'Approve',
|
|
128
|
+
rejectLabel: args.rejectLabel ?? defaults.rejectLabel ?? 'Reject',
|
|
129
|
+
// `??`, not `||`: an explicit 0 means "do not wait", and `||` would turn
|
|
130
|
+
// that into five minutes.
|
|
131
|
+
timeoutMs: (args.timeoutSeconds ?? defaults.timeoutSeconds ?? 300) * 1000,
|
|
132
|
+
});
|
|
133
|
+
return approved ? APPROVED : REJECTED;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// Returned, not thrown: a thrown tool error ends the agent run, and an
|
|
137
|
+
// agent that cannot ask permission should be able to say so and stop.
|
|
138
|
+
return errorResult(error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function runAlert(client, args, defaults = {}) {
|
|
142
|
+
try {
|
|
143
|
+
return alertResult(await client.send(args.title, {
|
|
144
|
+
description: args.description,
|
|
145
|
+
severity: args.severity ?? defaults.severity ?? 'MEDIUM',
|
|
146
|
+
customId: args.customId,
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
// Includes M91DeliveryError, whose message already says nobody's phone
|
|
151
|
+
// rang. A model reading "error:" and retrying is right there; a model
|
|
152
|
+
// reading "sent" and stopping is not.
|
|
153
|
+
return errorResult(error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Does this call need a human? With no `tools`, every call that reaches the gate does. */
|
|
157
|
+
export async function needsHuman(options, toolName, input) {
|
|
158
|
+
const { tools } = options;
|
|
159
|
+
if (tools === undefined)
|
|
160
|
+
return true;
|
|
161
|
+
if (typeof tools === 'function')
|
|
162
|
+
return Boolean(await tools(toolName, input));
|
|
163
|
+
return tools.includes(toolName);
|
|
164
|
+
}
|
|
165
|
+
/** A customId M91 accepts, stable across a framework's retries of the same call. */
|
|
166
|
+
export function gateCustomId(call) {
|
|
167
|
+
const raw = `tool-${call.toolName}-${call.callId}`;
|
|
168
|
+
return raw.replace(/[^A-Za-z0-9_-]/g, '-').slice(0, 100);
|
|
169
|
+
}
|
|
170
|
+
function defaultQuestion(call) {
|
|
171
|
+
let args;
|
|
172
|
+
try {
|
|
173
|
+
args = JSON.stringify(call.input, null, 2) ?? '';
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
args = String(call.input);
|
|
177
|
+
}
|
|
178
|
+
if (args.length > 1500)
|
|
179
|
+
args = `${args.slice(0, 1500)}\n… (truncated)`;
|
|
180
|
+
return {
|
|
181
|
+
title: `Allow the agent to run ${call.toolName}?`,
|
|
182
|
+
description: `The agent wants to call ${call.toolName} with:\n${args}`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Ring a phone about a tool call and wait for the answer.
|
|
187
|
+
*
|
|
188
|
+
* Fails closed: an M91 error, an unreachable channel or a timeout is a no,
|
|
189
|
+
* with the reason spelled out, never a silent yes.
|
|
190
|
+
*/
|
|
191
|
+
export async function decideToolCall(client, call, options = {}) {
|
|
192
|
+
const question = options.describe ? options.describe(call) : defaultQuestion(call);
|
|
193
|
+
try {
|
|
194
|
+
const approved = await client.requestApproval(question.title, {
|
|
195
|
+
customId: gateCustomId(call),
|
|
196
|
+
description: question.description,
|
|
197
|
+
severity: options.severity ?? 'HIGH',
|
|
198
|
+
approveLabel: options.approveLabel ?? 'Approve',
|
|
199
|
+
rejectLabel: options.rejectLabel ?? 'Reject',
|
|
200
|
+
timeoutMs: (options.timeoutSeconds ?? 300) * 1000,
|
|
201
|
+
});
|
|
202
|
+
return approved
|
|
203
|
+
? { approved: true, reason: 'Approved by a human via M91.' }
|
|
204
|
+
: {
|
|
205
|
+
approved: false,
|
|
206
|
+
reason: 'A human rejected this action via M91 (or did not answer in time). ' +
|
|
207
|
+
'Do not retry it; tell the user it was not approved.',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
approved: false,
|
|
213
|
+
reason: `Could not get human approval via M91, so the action was not run. ${errorResult(error)}`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=specs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specs.js","sourceRoot":"","sources":["../../../src/integrations/specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
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
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
23
|
+
* import { createM91ApprovalTool, createM91AlertTool } from '@msg91comm/m91/integrations/vercel-ai';
|
|
24
|
+
*
|
|
25
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
26
|
+
*
|
|
27
|
+
* const { text } = await generateText({
|
|
28
|
+
* model: openai('gpt-4o'),
|
|
29
|
+
* tools: {
|
|
30
|
+
* m91_request_approval: await createM91ApprovalTool(client),
|
|
31
|
+
* m91_send_alert: await createM91AlertTool(client),
|
|
32
|
+
* },
|
|
33
|
+
* stopWhen: isStepCount(10),
|
|
34
|
+
* prompt: 'Look into order ORD-40122 and refund it if it checks out.',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* The factories are **async** because `ai` is an optional peer dependency,
|
|
39
|
+
* imported only when one of these is called — `npm install @msg91comm/m91` stays at zero
|
|
40
|
+
* dependencies.
|
|
41
|
+
*/
|
|
42
|
+
import type { Tool, ToolApprovalStatus, ToolSet } from 'ai';
|
|
43
|
+
import type { M91Client } from '../client.js';
|
|
44
|
+
import { type AlertArgs, type AlertDefaults, type ApprovalArgs, type ApprovalDefaults, type GateOptions } from './specs.js';
|
|
45
|
+
/**
|
|
46
|
+
* The blocking approval gate. Returns `"approved"`, `"rejected"` or
|
|
47
|
+
* `"error: …"`, and nothing but a real human approval returns `"approved"`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createM91ApprovalTool(client: M91Client, defaults?: ApprovalDefaults): Promise<Tool<ApprovalArgs, string>>;
|
|
50
|
+
/** The fire-and-forget alert. Returns `"sent:delivered=N"` or `"error: …"`. */
|
|
51
|
+
export declare function createM91AlertTool(client: M91Client, defaults?: AlertDefaults): Promise<Tool<AlertArgs, string>>;
|
|
52
|
+
/** Both, keyed by tool name, ready to spread into `tools: {…}`. */
|
|
53
|
+
export declare function createM91Tools(client: M91Client, defaults?: ApprovalDefaults & AlertDefaults): Promise<ToolSet>;
|
|
54
|
+
/** The fields of an AI SDK tool call the gate reads. */
|
|
55
|
+
interface ApprovalRequestLike {
|
|
56
|
+
toolCall: {
|
|
57
|
+
toolName: string;
|
|
58
|
+
input: unknown;
|
|
59
|
+
toolCallId: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A `toolApproval` function for `generateText` / `streamText` / `ToolLoopAgent`.
|
|
64
|
+
*
|
|
65
|
+
* Calls to the tools in `options.tools` block while M91 rings a phone, then run
|
|
66
|
+
* if a person approves and come back to the model as denied otherwise. Every
|
|
67
|
+
* other call is `not-applicable` and runs as normal. Not synchronous: it waits
|
|
68
|
+
* up to `timeoutSeconds` (default 300) for an answer.
|
|
69
|
+
*/
|
|
70
|
+
export declare function createM91ToolApproval(client: M91Client, options: GateOptions & {
|
|
71
|
+
tools: NonNullable<GateOptions['tools']>;
|
|
72
|
+
}): (request: ApprovalRequestLike) => Promise<ToolApprovalStatus>;
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=vercel-ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAC;AAqBpB;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CASrC;AAED,+EAA+E;AAC/E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,aAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAOlC;AAED,mEAAmE;AACnE,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAgB,GAAG,aAAkB,GAC9C,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wDAAwD;AACxD,UAAU,mBAAmB;IAC3B,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;CAAE,GAClE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAY/D"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
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
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
23
|
+
* import { createM91ApprovalTool, createM91AlertTool } from '@msg91comm/m91/integrations/vercel-ai';
|
|
24
|
+
*
|
|
25
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
26
|
+
*
|
|
27
|
+
* const { text } = await generateText({
|
|
28
|
+
* model: openai('gpt-4o'),
|
|
29
|
+
* tools: {
|
|
30
|
+
* m91_request_approval: await createM91ApprovalTool(client),
|
|
31
|
+
* m91_send_alert: await createM91AlertTool(client),
|
|
32
|
+
* },
|
|
33
|
+
* stopWhen: isStepCount(10),
|
|
34
|
+
* prompt: 'Look into order ORD-40122 and refund it if it checks out.',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* The factories are **async** because `ai` is an optional peer dependency,
|
|
39
|
+
* imported only when one of these is called — `npm install @msg91comm/m91` stays at zero
|
|
40
|
+
* dependencies.
|
|
41
|
+
*/
|
|
42
|
+
import { ALERT_DESCRIPTION, ALERT_SCHEMA, APPROVAL_DESCRIPTION, APPROVAL_SCHEMA, decideToolCall, needsHuman, runAlert, runApproval, } from './specs.js';
|
|
43
|
+
async function aiModule() {
|
|
44
|
+
try {
|
|
45
|
+
// A variable specifier: an optional peer dependency must not be a build
|
|
46
|
+
// requirement for this package.
|
|
47
|
+
const specifier = 'ai';
|
|
48
|
+
return (await import(specifier));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error('The Vercel AI SDK integration needs the `ai` package. Install it with: npm install ai', { cause: error });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The blocking approval gate. Returns `"approved"`, `"rejected"` or
|
|
56
|
+
* `"error: …"`, and nothing but a real human approval returns `"approved"`.
|
|
57
|
+
*/
|
|
58
|
+
export async function createM91ApprovalTool(client, defaults = {}) {
|
|
59
|
+
const { tool, jsonSchema } = await aiModule();
|
|
60
|
+
return tool({
|
|
61
|
+
description: APPROVAL_DESCRIPTION,
|
|
62
|
+
// JSON Schema rather than zod, so this adapter adds no dependency of its own
|
|
63
|
+
// and the argument docs stay in one file shared with every other framework.
|
|
64
|
+
inputSchema: jsonSchema(APPROVAL_SCHEMA),
|
|
65
|
+
execute: (args) => runApproval(client, args, defaults),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** The fire-and-forget alert. Returns `"sent:delivered=N"` or `"error: …"`. */
|
|
69
|
+
export async function createM91AlertTool(client, defaults = {}) {
|
|
70
|
+
const { tool, jsonSchema } = await aiModule();
|
|
71
|
+
return tool({
|
|
72
|
+
description: ALERT_DESCRIPTION,
|
|
73
|
+
inputSchema: jsonSchema(ALERT_SCHEMA),
|
|
74
|
+
execute: (args) => runAlert(client, args, defaults),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** Both, keyed by tool name, ready to spread into `tools: {…}`. */
|
|
78
|
+
export async function createM91Tools(client, defaults = {}) {
|
|
79
|
+
return {
|
|
80
|
+
m91_request_approval: await createM91ApprovalTool(client, defaults),
|
|
81
|
+
m91_send_alert: await createM91AlertTool(client, defaults),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A `toolApproval` function for `generateText` / `streamText` / `ToolLoopAgent`.
|
|
86
|
+
*
|
|
87
|
+
* Calls to the tools in `options.tools` block while M91 rings a phone, then run
|
|
88
|
+
* if a person approves and come back to the model as denied otherwise. Every
|
|
89
|
+
* other call is `not-applicable` and runs as normal. Not synchronous: it waits
|
|
90
|
+
* up to `timeoutSeconds` (default 300) for an answer.
|
|
91
|
+
*/
|
|
92
|
+
export function createM91ToolApproval(client, options) {
|
|
93
|
+
return async ({ toolCall }) => {
|
|
94
|
+
if (!(await needsHuman(options, toolCall.toolName, toolCall.input)))
|
|
95
|
+
return 'not-applicable';
|
|
96
|
+
const decision = await decideToolCall(client, { toolName: toolCall.toolName, input: toolCall.input, callId: toolCall.toolCallId }, options);
|
|
97
|
+
return decision.approved
|
|
98
|
+
? { type: 'approved', reason: decision.reason }
|
|
99
|
+
: { type: 'denied', reason: decision.reason };
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=vercel-ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-ai.js","sourceRoot":"","sources":["../../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAKH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,eAAe,EAMf,cAAc,EACd,UAAU,EACV,QAAQ,EACR,WAAW,GACZ,MAAM,YAAY,CAAC;AAOpB,KAAK,UAAU,QAAQ;IACrB,IAAI,CAAC;QACH,wEAAwE;QACxE,gCAAgC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC;QACvB,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAwB,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,uFAAuF,EACvF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAiB,EACjB,WAA6B,EAAE;IAE/B,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC9C,OAAO,IAAI,CAAC;QACV,WAAW,EAAE,oBAAoB;QACjC,6EAA6E;QAC7E,4EAA4E;QAC5E,WAAW,EAAE,UAAU,CAAC,eAAe,CAAC;QACxC,OAAO,EAAE,CAAC,IAAkB,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;KACrE,CAA+B,CAAC;AACnC,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAiB,EACjB,WAA0B,EAAE;IAE5B,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC9C,OAAO,IAAI,CAAC;QACV,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC;QACrC,OAAO,EAAE,CAAC,IAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;KAC/D,CAA4B,CAAC;AAChC,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAiB,EACjB,WAA6C,EAAE;IAE/C,OAAO;QACL,oBAAoB,EAAE,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC;QACnE,cAAc,EAAE,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC;KAC3D,CAAC;AACJ,CAAC;AAOD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,OAAmE;IAEnE,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC5B,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,gBAAgB,CAAC;QAC7F,MAAM,QAAQ,GAAG,MAAM,cAAc,CACnC,MAAM,EACN,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,EAAE,EACnF,OAAO,CACR,CAAC;QACF,OAAO,QAAQ,CAAC,QAAQ;YACtB,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE;YAC/C,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
/** The slice of zod this file uses; v3 and v4 both satisfy it. */
|
|
9
|
+
export interface ZodLike {
|
|
10
|
+
string(): any;
|
|
11
|
+
number(): any;
|
|
12
|
+
enum(values: readonly [string, ...string[]]): any;
|
|
13
|
+
object(shape: Record<string, any>): any;
|
|
14
|
+
}
|
|
15
|
+
export declare function approvalShape(z: ZodLike): Record<string, any>;
|
|
16
|
+
export declare function alertShape(z: ZodLike): Record<string, any>;
|
|
17
|
+
/** zod is an optional peer dependency: import it only when asked, and say so if it is missing. */
|
|
18
|
+
export declare function loadZod(framework: string): Promise<ZodLike>;
|
|
19
|
+
//# sourceMappingURL=zod-shapes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-shapes.d.ts","sourceRoot":"","sources":["../../../src/integrations/zod-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,kEAAkE;AAClE,MAAM,WAAW,OAAO;IACtB,MAAM,IAAI,GAAG,CAAC;IACd,MAAM,IAAI,GAAG,CAAC;IACd,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;CACzC;AAID,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAW7D;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQ1D;AAED,kGAAkG;AAClG,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUjE"}
|