@mindstudio-ai/agent 0.1.106 → 0.1.108
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/README.md +12 -3
- package/dist/cli.js +334 -61
- package/dist/index.d.ts +286 -4
- package/dist/index.js +321 -46
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +334 -61
- package/llms.txt +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -102,9 +102,12 @@ function runWithContext(ctx, fn) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/db-ws.ts
|
|
105
|
-
var
|
|
105
|
+
var DB_WS_SUBPROTOCOL = "ms-db-v2";
|
|
106
|
+
var WS_ACK_TIMEOUT_MS = 2e4;
|
|
107
|
+
var WS_IDLE_TIMEOUT_MS = 3e5;
|
|
106
108
|
var WS_OPEN_TIMEOUT_MS = 5e3;
|
|
107
109
|
var WS_OPEN = 1;
|
|
110
|
+
var WS_CHUNK_CHARS = 256 * 1024;
|
|
108
111
|
var WS_MAX_FRAME_CHARS = 9e5;
|
|
109
112
|
var DbWsTransportError = class extends Error {
|
|
110
113
|
constructor(message, sent = false) {
|
|
@@ -117,13 +120,86 @@ function isReadOnlySql(sql) {
|
|
|
117
120
|
const head = sql.trimStart().slice(0, 8).toUpperCase();
|
|
118
121
|
return head.startsWith("SELECT") || head.startsWith("PRAGMA") || head.startsWith("EXPLAIN");
|
|
119
122
|
}
|
|
120
|
-
var
|
|
123
|
+
var conn = null;
|
|
121
124
|
var opening = null;
|
|
122
125
|
var idCounter = 0;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
function clearPendingTimer(p) {
|
|
127
|
+
if (p.timer !== null) {
|
|
128
|
+
clearTimeout(p.timer);
|
|
129
|
+
p.timer = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function forget(self, id) {
|
|
133
|
+
self.pending.delete(id);
|
|
134
|
+
self.inbound.delete(id);
|
|
135
|
+
if (self.retired && self.pending.size === 0) {
|
|
136
|
+
try {
|
|
137
|
+
self.ws.close();
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function retire(self, reason) {
|
|
143
|
+
if (self.retired) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
self.retired = true;
|
|
147
|
+
if (conn === self) {
|
|
148
|
+
conn = null;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
console.warn(
|
|
152
|
+
`[mindstudio] db: retiring WebSocket after ${reason}; next query reconnects.`
|
|
153
|
+
);
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
if (self.pending.size === 0) {
|
|
157
|
+
try {
|
|
158
|
+
self.ws.close();
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function armIdleTimer(self, id, p) {
|
|
164
|
+
clearPendingTimer(p);
|
|
165
|
+
p.timer = setTimeout(() => {
|
|
166
|
+
forget(self, id);
|
|
167
|
+
retire(self, "a reply went silent");
|
|
168
|
+
p.reject(new DbWsTransportError("ws reply went silent", true));
|
|
169
|
+
}, WS_IDLE_TIMEOUT_MS);
|
|
170
|
+
}
|
|
171
|
+
function deliver(self, msg) {
|
|
172
|
+
const p = self.pending.get(msg.id);
|
|
173
|
+
if (!p) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
clearPendingTimer(p);
|
|
177
|
+
forget(self, msg.id);
|
|
178
|
+
if (msg.error) {
|
|
179
|
+
p.reject(
|
|
180
|
+
new MindStudioError(
|
|
181
|
+
`[db] ${msg.error.message || "Database query failed"}`,
|
|
182
|
+
msg.error.code || "db_query_error",
|
|
183
|
+
400,
|
|
184
|
+
msg.error
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
p.resolve(msg.results);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function failTransport(self, id, err) {
|
|
192
|
+
const p = self.pending.get(id);
|
|
193
|
+
if (!p) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
clearPendingTimer(p);
|
|
197
|
+
forget(self, id);
|
|
198
|
+
p.reject(err);
|
|
199
|
+
}
|
|
200
|
+
function getOrOpen(url, token) {
|
|
201
|
+
if (conn && !conn.retired && conn.ws.readyState === WS_OPEN) {
|
|
202
|
+
return Promise.resolve(conn);
|
|
127
203
|
}
|
|
128
204
|
if (opening) {
|
|
129
205
|
return opening;
|
|
@@ -136,17 +212,22 @@ function getOrOpen(url) {
|
|
|
136
212
|
}
|
|
137
213
|
opening = new Promise((resolve, reject) => {
|
|
138
214
|
let ws;
|
|
215
|
+
let self = null;
|
|
139
216
|
const fail = (err) => {
|
|
140
217
|
clearTimeout(openTimer);
|
|
141
|
-
if (
|
|
142
|
-
|
|
218
|
+
if (self) {
|
|
219
|
+
self.retired = true;
|
|
220
|
+
if (conn === self) {
|
|
221
|
+
conn = null;
|
|
222
|
+
}
|
|
223
|
+
for (const [, p] of self.pending) {
|
|
224
|
+
clearPendingTimer(p);
|
|
225
|
+
p.reject(new DbWsTransportError(err.message, true));
|
|
226
|
+
}
|
|
227
|
+
self.pending.clear();
|
|
228
|
+
self.inbound.clear();
|
|
143
229
|
}
|
|
144
230
|
opening = null;
|
|
145
|
-
for (const [, p] of pending) {
|
|
146
|
-
clearTimeout(p.timer);
|
|
147
|
-
p.reject(new DbWsTransportError(err.message, true));
|
|
148
|
-
}
|
|
149
|
-
pending.clear();
|
|
150
231
|
reject(err);
|
|
151
232
|
};
|
|
152
233
|
const openTimer = setTimeout(() => {
|
|
@@ -157,16 +238,26 @@ function getOrOpen(url) {
|
|
|
157
238
|
fail(new DbWsTransportError("ws open timed out"));
|
|
158
239
|
}, WS_OPEN_TIMEOUT_MS);
|
|
159
240
|
try {
|
|
160
|
-
ws = new WebSocketCtor(url);
|
|
241
|
+
ws = new WebSocketCtor(url, ["auth", token, DB_WS_SUBPROTOCOL]);
|
|
161
242
|
} catch (err) {
|
|
162
243
|
fail(new DbWsTransportError(err?.message || "ws construct failed"));
|
|
163
244
|
return;
|
|
164
245
|
}
|
|
246
|
+
self = {
|
|
247
|
+
ws,
|
|
248
|
+
pending: /* @__PURE__ */ new Map(),
|
|
249
|
+
chunked: false,
|
|
250
|
+
inbound: /* @__PURE__ */ new Map(),
|
|
251
|
+
acksSeen: false,
|
|
252
|
+
retired: false
|
|
253
|
+
};
|
|
254
|
+
const c = self;
|
|
165
255
|
ws.onopen = () => {
|
|
166
256
|
clearTimeout(openTimer);
|
|
167
|
-
|
|
257
|
+
c.chunked = ws.protocol === DB_WS_SUBPROTOCOL;
|
|
258
|
+
conn = c;
|
|
168
259
|
opening = null;
|
|
169
|
-
resolve(
|
|
260
|
+
resolve(c);
|
|
170
261
|
};
|
|
171
262
|
ws.onmessage = (ev) => {
|
|
172
263
|
let msg;
|
|
@@ -177,26 +268,54 @@ function getOrOpen(url) {
|
|
|
177
268
|
} catch {
|
|
178
269
|
return;
|
|
179
270
|
}
|
|
180
|
-
if (!msg || msg.
|
|
271
|
+
if (!msg || msg.id == null) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (msg.t === "db-ack") {
|
|
275
|
+
c.acksSeen = true;
|
|
276
|
+
const p = c.pending.get(msg.id);
|
|
277
|
+
if (p && !p.acked) {
|
|
278
|
+
p.acked = true;
|
|
279
|
+
armIdleTimer(c, msg.id, p);
|
|
280
|
+
}
|
|
181
281
|
return;
|
|
182
282
|
}
|
|
183
|
-
|
|
184
|
-
|
|
283
|
+
if (msg.t === "db-chunk") {
|
|
284
|
+
const p = c.pending.get(msg.id);
|
|
285
|
+
if (!p || typeof msg.data !== "string") {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const parts = c.inbound.get(msg.id);
|
|
289
|
+
if (parts) {
|
|
290
|
+
parts.push(msg.data);
|
|
291
|
+
} else {
|
|
292
|
+
c.inbound.set(msg.id, [msg.data]);
|
|
293
|
+
}
|
|
294
|
+
armIdleTimer(c, msg.id, p);
|
|
185
295
|
return;
|
|
186
296
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
297
|
+
if (msg.t === "db-end") {
|
|
298
|
+
const parts = c.inbound.get(msg.id);
|
|
299
|
+
if (!parts) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
c.inbound.delete(msg.id);
|
|
303
|
+
let assembled;
|
|
304
|
+
try {
|
|
305
|
+
assembled = JSON.parse(parts.join(""));
|
|
306
|
+
} catch {
|
|
307
|
+
failTransport(
|
|
308
|
+
c,
|
|
309
|
+
msg.id,
|
|
310
|
+
new DbWsTransportError("chunked reply did not parse", true)
|
|
311
|
+
);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
deliver(c, assembled);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (msg.t === "db-result") {
|
|
318
|
+
deliver(c, msg);
|
|
200
319
|
}
|
|
201
320
|
};
|
|
202
321
|
ws.onerror = () => fail(new DbWsTransportError("ws error"));
|
|
@@ -204,28 +323,59 @@ function getOrOpen(url) {
|
|
|
204
323
|
});
|
|
205
324
|
return opening;
|
|
206
325
|
}
|
|
326
|
+
function* outboundFrames(id, json) {
|
|
327
|
+
if (json.length <= WS_CHUNK_CHARS) {
|
|
328
|
+
yield json;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
let seq = 0;
|
|
332
|
+
for (let offset = 0; offset < json.length; offset += WS_CHUNK_CHARS) {
|
|
333
|
+
yield JSON.stringify({
|
|
334
|
+
t: "db-chunk",
|
|
335
|
+
id,
|
|
336
|
+
seq: seq++,
|
|
337
|
+
data: json.slice(offset, offset + WS_CHUNK_CHARS)
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
yield JSON.stringify({ t: "db-end", id });
|
|
341
|
+
}
|
|
207
342
|
async function executeDbBatchOverWs(url, token, databaseId, queries) {
|
|
208
343
|
const id = ++idCounter;
|
|
209
344
|
const frame = JSON.stringify({ t: "db", id, token, databaseId, queries });
|
|
210
|
-
|
|
345
|
+
const self = await getOrOpen(url, token);
|
|
346
|
+
if (!self.chunked && frame.length > WS_MAX_FRAME_CHARS) {
|
|
211
347
|
throw new DbWsTransportError("payload too large for ws");
|
|
212
348
|
}
|
|
213
|
-
const ws = await getOrOpen(url);
|
|
214
349
|
return await new Promise((resolve, reject) => {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
350
|
+
const pending = { resolve, reject, timer: null, acked: false };
|
|
351
|
+
if (self.acksSeen) {
|
|
352
|
+
pending.timer = setTimeout(() => {
|
|
353
|
+
forget(self, id);
|
|
354
|
+
retire(self, "a request went unacknowledged");
|
|
355
|
+
reject(new DbWsTransportError("ws ack timed out", true));
|
|
356
|
+
}, WS_ACK_TIMEOUT_MS);
|
|
357
|
+
} else {
|
|
358
|
+
armIdleTimer(self, id, pending);
|
|
359
|
+
}
|
|
360
|
+
self.pending.set(id, pending);
|
|
361
|
+
let started = false;
|
|
362
|
+
let complete = false;
|
|
220
363
|
try {
|
|
221
|
-
|
|
364
|
+
for (const out of outboundFrames(id, frame)) {
|
|
365
|
+
self.ws.send(out);
|
|
366
|
+
started = true;
|
|
367
|
+
}
|
|
368
|
+
complete = true;
|
|
222
369
|
} catch (err) {
|
|
223
|
-
|
|
224
|
-
|
|
370
|
+
clearPendingTimer(pending);
|
|
371
|
+
forget(self, id);
|
|
372
|
+
if (started) {
|
|
373
|
+
retire(self, "a partial request upload failed");
|
|
374
|
+
}
|
|
225
375
|
reject(
|
|
226
376
|
new DbWsTransportError(
|
|
227
377
|
`ws send failed: ${err?.message || "unknown"}`,
|
|
228
|
-
|
|
378
|
+
complete
|
|
229
379
|
)
|
|
230
380
|
);
|
|
231
381
|
}
|
|
@@ -2814,6 +2964,50 @@ function createAnalytics(call) {
|
|
|
2814
2964
|
};
|
|
2815
2965
|
}
|
|
2816
2966
|
|
|
2967
|
+
// src/email/index.ts
|
|
2968
|
+
var iso = (v) => v === void 0 ? void 0 : v instanceof Date ? v.toISOString() : v;
|
|
2969
|
+
function createEmail(call) {
|
|
2970
|
+
return {
|
|
2971
|
+
stats(options) {
|
|
2972
|
+
return call("stats", {
|
|
2973
|
+
start: iso(options?.start),
|
|
2974
|
+
end: iso(options?.end),
|
|
2975
|
+
buckets: options?.buckets
|
|
2976
|
+
});
|
|
2977
|
+
},
|
|
2978
|
+
messages(options) {
|
|
2979
|
+
return call("messages", {
|
|
2980
|
+
...options,
|
|
2981
|
+
start: iso(options?.start),
|
|
2982
|
+
end: iso(options?.end)
|
|
2983
|
+
});
|
|
2984
|
+
},
|
|
2985
|
+
message(messageId) {
|
|
2986
|
+
return call("message", { messageId });
|
|
2987
|
+
},
|
|
2988
|
+
batches(options) {
|
|
2989
|
+
return call("batches", {
|
|
2990
|
+
start: iso(options?.start),
|
|
2991
|
+
end: iso(options?.end),
|
|
2992
|
+
limit: options?.limit,
|
|
2993
|
+
offset: options?.offset
|
|
2994
|
+
});
|
|
2995
|
+
},
|
|
2996
|
+
batch(batchId) {
|
|
2997
|
+
return call("batch", { batchId });
|
|
2998
|
+
},
|
|
2999
|
+
suppressions(options) {
|
|
3000
|
+
return call("suppressions", options ?? {});
|
|
3001
|
+
},
|
|
3002
|
+
suppress(email2) {
|
|
3003
|
+
return call("suppress", { email: email2 });
|
|
3004
|
+
},
|
|
3005
|
+
unsuppress(email2) {
|
|
3006
|
+
return call("unsuppress", { email: email2 });
|
|
3007
|
+
}
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
|
|
2817
3011
|
// src/task/index.ts
|
|
2818
3012
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2819
3013
|
|
|
@@ -3797,7 +3991,8 @@ var stepMetadata = {
|
|
|
3797
3991
|
- connectionId can be a comma-separated list to send to multiple recipients.
|
|
3798
3992
|
- The special connectionId "trigger_email" uses the email address that triggered the workflow.
|
|
3799
3993
|
- Sends default to category "marketing": every message carries a one-click unsubscribe header pointing at the app's own domain, and recipients who previously unsubscribed from this app are skipped and returned in "suppressed". Because the unsubscribe link is per-recipient, a marketing send delivers one separate message per recipient and cc/bcc are folded into that list.
|
|
3800
|
-
- Set category to "transactional" for receipts, alerts, password resets and anything a recipient must not be able to opt out of. Transactional sends carry no unsubscribe header, ignore the unsubscribe list, and keep cc/bcc on a single message \u2014 use it whenever cc/bcc semantics matter
|
|
3994
|
+
- Set category to "transactional" for receipts, alerts, password resets and anything a recipient must not be able to opt out of. Transactional sends carry no unsubscribe header, ignore the unsubscribe list, and keep cc/bcc on a single message \u2014 use it whenever cc/bcc semantics matter.
|
|
3995
|
+
- The subject is stored verbatim in the app's delivery log, which every workspace member can read for 90 days. Never put a secret in the subject \u2014 no one-time codes, no magic-link tokens, no password-reset tokens. Put those in the body, which is never stored.`,
|
|
3801
3996
|
inputSchema: { "type": "object", "properties": { "subject": { "type": "string", "description": "Email subject line" }, "body": { "type": "string", "description": "Email body content (plain text, markdown, HTML, or a CDN URL to an HTML file)" }, "to": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "cc": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "bcc": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "connectionId": { "type": "string", "description": "OAuth connection ID(s) for the recipient(s), comma-separated for multiple" }, "generateHtml": { "type": "boolean", "description": "When true, auto-convert the body text into a styled HTML email using AI" }, "generateHtmlInstructions": { "type": "string", "description": "Natural language instructions for the HTML generation style" }, "generateHtmlModelOverride": { "type": "object", "properties": { "model": { "type": "string", "description": 'Model identifier (e.g. "gpt-4", "claude-3-opus")' }, "temperature": { "type": "number", "description": "Sampling temperature for the model (0-2)" }, "maxResponseTokens": { "type": "number", "description": "Maximum number of tokens in the model's response" }, "ignorePreamble": { "type": "boolean", "description": "Whether to skip the system preamble/instructions" }, "userMessagePreprocessor": { "type": "object", "properties": { "dataSource": { "type": "string", "description": "Data source identifier for the preprocessor" }, "messageTemplate": { "type": "string", "description": "Template string applied to user messages before sending to the model" }, "maxResults": { "type": "number", "description": "Maximum number of results to include from the data source" }, "enabled": { "type": "boolean", "description": "Whether the preprocessor is active" }, "shouldInherit": { "type": "boolean", "description": "Whether child steps should inherit this preprocessor configuration" } }, "description": "Preprocessor applied to user messages before sending to the model" }, "preamble": { "type": "string", "description": "System preamble/instructions for the model" }, "multiModelEnabled": { "type": "boolean", "description": "Whether multi-model candidate generation is enabled" }, "editResponseEnabled": { "type": "boolean", "description": "Whether the user can edit the model's response" }, "config": { "type": "object", "properties": {}, "required": [], "description": "Additional model-specific configuration" } }, "required": ["model", "temperature", "maxResponseTokens"], "description": "Model settings override for HTML generation" }, "attachments": { "type": "array", "items": { "anyOf": [{ "type": "string" }, { "type": "object", "properties": { "url": { "type": "string" }, "filename": { "type": "string" }, "contentType": { "type": "string" } }, "required": ["url"] }] }, "description": "Files to attach: each entry is a URL string, or an object `{ url, filename?, contentType? }` to control the displayed filename and MIME type." }, "from": { "type": "string", "description": "Custom sender handle \u2014 only for apps with a custom domain or subdomain. Bare handle (`support`), full `support@your-domain.com`, or `Name <support@your-domain.com>`. Must resolve to a domain the app owns." }, "bodyType": { "enum": ["auto", "html", "markdown", "text"], "type": "string", "description": "How to interpret `body`: `auto` (default \u2014 detect HTML, else render markdown), `html` (send as-is), `markdown` (render to HTML), or `text` (send as plain text only, no HTML part). Every send includes a text/plain alternative." }, "text": { "type": "string", "description": "Explicit text/plain alternative body. Auto-derived from `body` if omitted." }, "replyTo": { "type": "string", "description": "Reply-To address for the email." }, "inReplyTo": { "type": "string", "description": "Message-ID this email replies to, for inbox threading (In-Reply-To header)." }, "references": { "type": "array", "items": { "type": "string" }, "description": "Prior Message-IDs in the thread, for inbox threading (References header)." }, "category": { "enum": ["transactional", "marketing"], "type": "string", "description": "`marketing` (default) adds one-click unsubscribe headers and skips recipients who unsubscribed from this app. Use `transactional` for receipts, alerts and password resets: no unsubscribe header, never suppressed, and cc/bcc preserved on a single message." } }, "required": ["subject", "body"] },
|
|
3802
3997
|
outputSchema: { "type": "object", "properties": { "recipients": { "type": "array", "items": { "type": "string" }, "description": "To addresses the message was sent to." }, "cc": { "type": "array", "items": { "type": "string" }, "description": "Cc addresses on the message (empty if none)." }, "bcc": { "type": "array", "items": { "type": "string" }, "description": "Bcc addresses on the message (empty if none)." }, "from": { "type": "string", "description": "The resolved sender address the message went out as (auto-selected when no `from` is given)." }, "suppressed": { "type": "array", "items": { "type": "string" }, "description": "Recipients skipped because they unsubscribed from this app. Always empty for a `transactional` send." } }, "required": ["recipients", "cc", "bcc", "from", "suppressed"] }
|
|
3803
3998
|
},
|
|
@@ -4245,6 +4440,13 @@ function resolveStepType(name) {
|
|
|
4245
4440
|
}
|
|
4246
4441
|
function mapTools(tools) {
|
|
4247
4442
|
return tools.map((t) => {
|
|
4443
|
+
if (typeof t === "object" && "execute" in t) {
|
|
4444
|
+
return {
|
|
4445
|
+
name: t.name,
|
|
4446
|
+
description: t.description,
|
|
4447
|
+
inputSchema: t.inputSchema ?? { type: "object", properties: {} }
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4248
4450
|
if (typeof t === "object" && "appMethod" in t) {
|
|
4249
4451
|
return {
|
|
4250
4452
|
appMethod: t.appMethod,
|
|
@@ -4690,10 +4892,18 @@ ${structuredOutputExample}
|
|
|
4690
4892
|
const wireTools = mapTools(options.tools);
|
|
4691
4893
|
const toolKinds = /* @__PURE__ */ new Map();
|
|
4692
4894
|
const toolDefaults = /* @__PURE__ */ new Map();
|
|
4895
|
+
const functionTools = /* @__PURE__ */ new Map();
|
|
4896
|
+
for (const t of options.tools) {
|
|
4897
|
+
if (typeof t === "object" && "execute" in t) {
|
|
4898
|
+
functionTools.set(t.name, t.execute);
|
|
4899
|
+
}
|
|
4900
|
+
}
|
|
4693
4901
|
for (const t of wireTools) {
|
|
4694
4902
|
if ("appMethod" in t) {
|
|
4695
4903
|
toolKinds.set(t.appMethod, "method");
|
|
4696
4904
|
if (t.defaults) toolDefaults.set(t.appMethod, t.defaults);
|
|
4905
|
+
} else if ("name" in t) {
|
|
4906
|
+
toolKinds.set(t.name, "function");
|
|
4697
4907
|
} else {
|
|
4698
4908
|
toolKinds.set(t.stepType, "step");
|
|
4699
4909
|
if (t.defaults) toolDefaults.set(t.stepType, t.defaults);
|
|
@@ -4822,8 +5032,27 @@ ${structuredOutputExample}
|
|
|
4822
5032
|
const startTime = Date.now();
|
|
4823
5033
|
const defaults = toolDefaults.get(toolCall.name) || {};
|
|
4824
5034
|
const mergedInput = mergeToolInput(toolCall.input, defaults);
|
|
4825
|
-
const
|
|
4826
|
-
|
|
5035
|
+
const kind = toolKinds.get(toolCall.name);
|
|
5036
|
+
let result;
|
|
5037
|
+
if (kind === "function") {
|
|
5038
|
+
try {
|
|
5039
|
+
const output3 = await functionTools.get(toolCall.name)(
|
|
5040
|
+
mergedInput
|
|
5041
|
+
);
|
|
5042
|
+
result = { output: output3 ?? null, billingCost: 0, isError: false };
|
|
5043
|
+
} catch (err) {
|
|
5044
|
+
result = {
|
|
5045
|
+
output: {
|
|
5046
|
+
error: err instanceof Error ? err.message : "Function tool execution failed"
|
|
5047
|
+
},
|
|
5048
|
+
billingCost: 0,
|
|
5049
|
+
isError: true
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
} else {
|
|
5053
|
+
const execute = kind === "method" ? deps.executeMethodTool : deps.executeStepTool;
|
|
5054
|
+
result = await execute(toolCall.name, mergedInput);
|
|
5055
|
+
}
|
|
4827
5056
|
toolCallLog.push({
|
|
4828
5057
|
name: toolCall.name,
|
|
4829
5058
|
success: !result.isError,
|
|
@@ -5491,6 +5720,7 @@ var MindStudioAgent = class {
|
|
|
5491
5720
|
_dataSources;
|
|
5492
5721
|
_voice;
|
|
5493
5722
|
_analytics;
|
|
5723
|
+
_email;
|
|
5494
5724
|
/** @internal Auth type — 'internal' for CALLBACK_TOKEN (managed mode), 'apiKey' otherwise. */
|
|
5495
5725
|
_authType;
|
|
5496
5726
|
/** @internal Usage source sent on step executions (from MINDSTUDIO_REQUEST_SOURCE).
|
|
@@ -6074,6 +6304,13 @@ var MindStudioAgent = class {
|
|
|
6074
6304
|
);
|
|
6075
6305
|
} catch (err) {
|
|
6076
6306
|
if (err instanceof MindStudioError && err.code === TURN_UNAVAILABLE_CODE) {
|
|
6307
|
+
if (options.tools.some((t) => typeof t === "object" && "execute" in t)) {
|
|
6308
|
+
throw new MindStudioError(
|
|
6309
|
+
"[task] This server does not support the per-turn task endpoint required for function tools.",
|
|
6310
|
+
"task_function_tools_unsupported",
|
|
6311
|
+
404
|
|
6312
|
+
);
|
|
6313
|
+
}
|
|
6077
6314
|
const body = buildTaskRequestBody(options);
|
|
6078
6315
|
const result = options.onEvent ? await runTaskStream(httpConfig, body, options.onEvent) : await runTaskPoll(httpConfig, body);
|
|
6079
6316
|
if (options.outputSchema) {
|
|
@@ -6528,6 +6765,24 @@ var MindStudioAgent = class {
|
|
|
6528
6765
|
this._analyticsRequest.bind(this)
|
|
6529
6766
|
);
|
|
6530
6767
|
}
|
|
6768
|
+
/**
|
|
6769
|
+
* Outbound email: the per-recipient delivery log, blast stats, and this app's
|
|
6770
|
+
* unsubscribe list. Sending is the `sendEmail` action, not this namespace.
|
|
6771
|
+
*
|
|
6772
|
+
* @example
|
|
6773
|
+
* ```ts
|
|
6774
|
+
* import { email } from '@mindstudio-ai/agent';
|
|
6775
|
+
*
|
|
6776
|
+
* // A support lookup: did this person get their mail?
|
|
6777
|
+
* const { messages } = await email.messages({ recipient: 'a@b.com' });
|
|
6778
|
+
*
|
|
6779
|
+
* // How did the August newsletter do?
|
|
6780
|
+
* const blast = await email.batch('newsletter-2026-08');
|
|
6781
|
+
* ```
|
|
6782
|
+
*/
|
|
6783
|
+
get email() {
|
|
6784
|
+
return this._email ??= createEmail(this._emailRequest.bind(this));
|
|
6785
|
+
}
|
|
6531
6786
|
/**
|
|
6532
6787
|
* Jewel surfaces: arrival-shaped triggers (`propose`) and the app-native
|
|
6533
6788
|
* approval queue (`queue.list` / `queue.resolve`). See {@link JewelsApi}.
|
|
@@ -6666,6 +6921,18 @@ var MindStudioAgent = class {
|
|
|
6666
6921
|
* of surfacing as empty data. Deliberately single-shot and scoped to this
|
|
6667
6922
|
* namespace — the other brokered transports keep their semantics.
|
|
6668
6923
|
*/
|
|
6924
|
+
/**
|
|
6925
|
+
* Brokered email transport. No 429 retry, unlike analytics: this namespace
|
|
6926
|
+
* carries writes (suppress / unsuppress), and silently retrying a mutation is a
|
|
6927
|
+
* different risk from retrying an idempotent read. A caller that wants one can
|
|
6928
|
+
* retry a read itself.
|
|
6929
|
+
*/
|
|
6930
|
+
async _emailRequest(op, body) {
|
|
6931
|
+
return this._brokeredRequest("email", op, body, {
|
|
6932
|
+
fallbackMessage: "Email API call failed",
|
|
6933
|
+
fallbackCode: "email_error"
|
|
6934
|
+
});
|
|
6935
|
+
}
|
|
6669
6936
|
async _analyticsRequest(op, body) {
|
|
6670
6937
|
const call = () => this._brokeredRequest("analytics", op, body, {
|
|
6671
6938
|
fallbackMessage: "Analytics read failed",
|
|
@@ -7570,6 +7837,13 @@ var analytics = new Proxy({}, {
|
|
|
7570
7837
|
return typeof value === "function" ? value.bind(target) : value;
|
|
7571
7838
|
}
|
|
7572
7839
|
});
|
|
7840
|
+
var email = new Proxy({}, {
|
|
7841
|
+
get(_, prop) {
|
|
7842
|
+
const target = mindstudio.email;
|
|
7843
|
+
const value = Reflect.get(target, prop, target);
|
|
7844
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
7845
|
+
}
|
|
7846
|
+
});
|
|
7573
7847
|
var session = new Proxy(
|
|
7574
7848
|
{},
|
|
7575
7849
|
{
|
|
@@ -7598,6 +7872,7 @@ export {
|
|
|
7598
7872
|
db,
|
|
7599
7873
|
index_default as default,
|
|
7600
7874
|
defineJewel,
|
|
7875
|
+
email,
|
|
7601
7876
|
files,
|
|
7602
7877
|
getRequestContext,
|
|
7603
7878
|
mindstudio,
|