@mindstudio-ai/agent 0.1.108 → 0.1.110
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/dist/cli.js +87 -11
- package/dist/index.d.ts +181 -12
- package/dist/index.js +79 -3
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +87 -11
- package/llms.txt +2 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -109,6 +109,13 @@ declare class Store {
|
|
|
109
109
|
get access(): FileAccess;
|
|
110
110
|
/** Store bytes. Returns a {@link StoredFile} with a ready-to-use `url`. */
|
|
111
111
|
put(content: Buffer | Uint8Array | string, options?: PutOptions): Promise<StoredFile>;
|
|
112
|
+
/**
|
|
113
|
+
* Large-content half of {@link put}: mint a presigned POST scoped to exactly
|
|
114
|
+
* these bytes and send them direct to storage — the JSON transport's body
|
|
115
|
+
* limit never sees them. Same result contract as the small path; the final
|
|
116
|
+
* `head` keeps the returned metadata honest (server-observed size/type/time).
|
|
117
|
+
*/
|
|
118
|
+
private _putPresigned;
|
|
112
119
|
/** Read an object's bytes (backend / trusted context). */
|
|
113
120
|
get(key: string): Promise<Buffer>;
|
|
114
121
|
/** Metadata without downloading. Rejects if the object doesn't exist. */
|
|
@@ -2352,7 +2359,7 @@ interface SearchRan {
|
|
|
2352
2359
|
* vector, so they take effect on the next call and cost nothing. Anything that
|
|
2353
2360
|
* would require rebuilding the corpus — chunking, the embedding model, whether
|
|
2354
2361
|
* chunks are contextualized — is a property of the data source, configured
|
|
2355
|
-
* with `
|
|
2362
|
+
* with `remy-admin datasources config` rather than passed here.
|
|
2356
2363
|
*
|
|
2357
2364
|
* The defaults come from the data source's own configuration, so most callers
|
|
2358
2365
|
* should pass nothing.
|
|
@@ -2862,12 +2869,16 @@ interface Analytics {
|
|
|
2862
2869
|
}
|
|
2863
2870
|
|
|
2864
2871
|
/**
|
|
2865
|
-
* The `email` namespace: read and manage your app's OWN outbound email.
|
|
2872
|
+
* The `email` namespace: send, read and manage your app's OWN outbound email.
|
|
2866
2873
|
*
|
|
2867
|
-
*
|
|
2868
|
-
*
|
|
2869
|
-
*
|
|
2870
|
-
*
|
|
2874
|
+
* `email.send()` is the send path. The older `mindstudio.sendEmail({...})` action
|
|
2875
|
+
* still works and shares exactly the same implementation, so nothing breaks — but
|
|
2876
|
+
* `send()` is the one to reach for: it returns a proper receipt (including the
|
|
2877
|
+
* `batchId` you read stats back with) and it lives alongside everything you do
|
|
2878
|
+
* with the result.
|
|
2879
|
+
*
|
|
2880
|
+
* The rest of the namespace is everything *after* the send: what happened to each
|
|
2881
|
+
* message, how a campaign performed, and who has unsubscribed.
|
|
2871
2882
|
*
|
|
2872
2883
|
* The point is that you can build your own email admin screen inside your own app
|
|
2873
2884
|
* and never open the MindStudio dashboard. So this deliberately includes the
|
|
@@ -2875,7 +2886,7 @@ interface Analytics {
|
|
|
2875
2886
|
* unsubscribe list, this can do too.
|
|
2876
2887
|
*
|
|
2877
2888
|
* Every call is scoped to the executing app by its request token; there is no
|
|
2878
|
-
* appId parameter and no way to read another app
|
|
2889
|
+
* appId parameter and no way to read or send as another app.
|
|
2879
2890
|
*/
|
|
2880
2891
|
/** Where a message got to. See `EmailMessage.status`. */
|
|
2881
2892
|
type EmailMessageStatus =
|
|
@@ -3011,7 +3022,149 @@ interface EmailWindowOptions {
|
|
|
3011
3022
|
start?: string | Date;
|
|
3012
3023
|
end?: string | Date;
|
|
3013
3024
|
}
|
|
3025
|
+
/**
|
|
3026
|
+
* How a message body is interpreted.
|
|
3027
|
+
*
|
|
3028
|
+
* `auto` (the default) sniffs it: something that looks like HTML is sent as HTML,
|
|
3029
|
+
* anything else is rendered from Markdown. Every message goes out multipart with a
|
|
3030
|
+
* plain-text alternative derived from the body, which mail providers reward.
|
|
3031
|
+
*/
|
|
3032
|
+
type EmailBodyType = 'auto' | 'html' | 'markdown' | 'text';
|
|
3033
|
+
interface EmailAttachmentInput {
|
|
3034
|
+
url: string;
|
|
3035
|
+
/** Displayed filename. Derived from the URL when omitted. */
|
|
3036
|
+
filename?: string;
|
|
3037
|
+
contentType?: string;
|
|
3038
|
+
}
|
|
3039
|
+
interface EmailSendOptions {
|
|
3040
|
+
/** Visible recipients. Optional only if you supply `cc` or `bcc`. */
|
|
3041
|
+
to?: string | string[];
|
|
3042
|
+
cc?: string | string[];
|
|
3043
|
+
/**
|
|
3044
|
+
* Hidden recipients. With no `to`/`cc`, the To: header is addressed to your
|
|
3045
|
+
* app's own sender — the standard "undisclosed recipients" pattern — so
|
|
3046
|
+
* recipients can't see each other.
|
|
3047
|
+
*/
|
|
3048
|
+
bcc?: string | string[];
|
|
3049
|
+
subject: string;
|
|
3050
|
+
/** Plain text, Markdown, or HTML. See {@link EmailBodyType}. */
|
|
3051
|
+
body: string;
|
|
3052
|
+
bodyType?: EmailBodyType;
|
|
3053
|
+
/** Your own plain-text alternative, instead of the auto-derived one. */
|
|
3054
|
+
text?: string;
|
|
3055
|
+
attachments?: Array<string | EmailAttachmentInput>;
|
|
3056
|
+
/**
|
|
3057
|
+
* Sender handle — only if your app has a custom domain or platform subdomain.
|
|
3058
|
+
* `"support"`, `"support@your-domain.com"`, or `"Name <support@your-domain.com>"`.
|
|
3059
|
+
* The domain must be one your app owns or the send fails.
|
|
3060
|
+
*/
|
|
3061
|
+
from?: string;
|
|
3062
|
+
replyTo?: string;
|
|
3063
|
+
/** Message-ID this replies to, for threading in a shared inbox. */
|
|
3064
|
+
inReplyTo?: string;
|
|
3065
|
+
/** Prior Message-IDs in the thread. */
|
|
3066
|
+
references?: string[];
|
|
3067
|
+
/**
|
|
3068
|
+
* **Defaults to `marketing`**, which is the consequential choice here.
|
|
3069
|
+
*
|
|
3070
|
+
* `marketing` attaches a one-click unsubscribe header pointing at your own
|
|
3071
|
+
* domain and skips anyone who previously unsubscribed from your app. Because
|
|
3072
|
+
* that link is per-recipient, a marketing send delivers one separate message per
|
|
3073
|
+
* recipient and `cc`/`bcc` are folded into that list — so they come back empty.
|
|
3074
|
+
*
|
|
3075
|
+
* `transactional` is for receipts, alerts and password resets: no unsubscribe
|
|
3076
|
+
* header, the unsubscribe list is ignored, and `cc`/`bcc` stay on one shared
|
|
3077
|
+
* message. Use it whenever cc/bcc semantics actually matter.
|
|
3078
|
+
*/
|
|
3079
|
+
category?: 'transactional' | 'marketing';
|
|
3080
|
+
/**
|
|
3081
|
+
* Groups this send's per-recipient rows into one blast. Pass your own campaign
|
|
3082
|
+
* id (1–64 printable chars) to join our delivery stats onto your own tables with
|
|
3083
|
+
* no mapping; one is generated when omitted, and it's always returned.
|
|
3084
|
+
*/
|
|
3085
|
+
batchId?: string;
|
|
3086
|
+
}
|
|
3087
|
+
interface EmailSendResult {
|
|
3088
|
+
/** Addresses the message went to. */
|
|
3089
|
+
recipients: string[];
|
|
3090
|
+
/** Always empty for a marketing send — see `category`. */
|
|
3091
|
+
cc: string[];
|
|
3092
|
+
/** Always empty for a marketing send — see `category`. */
|
|
3093
|
+
bcc: string[];
|
|
3094
|
+
/** The resolved sender it went out as. */
|
|
3095
|
+
from: string;
|
|
3096
|
+
/**
|
|
3097
|
+
* Recipients skipped because they had unsubscribed from your app. **Not an
|
|
3098
|
+
* error** — the send succeeded and we honoured their opt-out. Always empty for
|
|
3099
|
+
* a transactional send.
|
|
3100
|
+
*
|
|
3101
|
+
* Worth surfacing to whoever triggered the send: "sent to 48 of 50, 2
|
|
3102
|
+
* unsubscribed" is the honest report, and silence here is what makes people
|
|
3103
|
+
* think mail vanished.
|
|
3104
|
+
*/
|
|
3105
|
+
suppressed: string[];
|
|
3106
|
+
/** Read stats back with `email.batch(batchId)`. */
|
|
3107
|
+
batchId: string;
|
|
3108
|
+
}
|
|
3109
|
+
/** Your app's sending allowance for the current window. */
|
|
3110
|
+
interface EmailQuota {
|
|
3111
|
+
/** What the numbers count — `recipients`. */
|
|
3112
|
+
unit: string;
|
|
3113
|
+
window: 'day' | 'month' | 'total';
|
|
3114
|
+
/** `null` means unlimited. */
|
|
3115
|
+
limit: number | null;
|
|
3116
|
+
used: number;
|
|
3117
|
+
/** `null` when `limit` is null. */
|
|
3118
|
+
remaining: number | null;
|
|
3119
|
+
/** When `used` resets. */
|
|
3120
|
+
resetsAt: string | null;
|
|
3121
|
+
}
|
|
3014
3122
|
interface Email {
|
|
3123
|
+
/**
|
|
3124
|
+
* Send an email.
|
|
3125
|
+
*
|
|
3126
|
+
* Two things to know before your first call. **`category` defaults to
|
|
3127
|
+
* `marketing`**, which fans a multi-recipient send out into one message each and
|
|
3128
|
+
* empties `cc`/`bcc` — pass `'transactional'` for receipts, alerts and codes.
|
|
3129
|
+
* And a non-empty `suppressed` in the result is a **success**, not a failure: it
|
|
3130
|
+
* lists people who had unsubscribed and were therefore skipped.
|
|
3131
|
+
*
|
|
3132
|
+
* Delivery is synchronous today: this resolves once the mail has been handed to
|
|
3133
|
+
* the provider, so a large marketing list takes a while. Outcomes (delivered,
|
|
3134
|
+
* bounced, complained) arrive later — read them with
|
|
3135
|
+
* `email.messages({ batchId })`.
|
|
3136
|
+
*
|
|
3137
|
+
* @example
|
|
3138
|
+
* ```ts
|
|
3139
|
+
* // A receipt: one message, cc preserved, no unsubscribe header.
|
|
3140
|
+
* await email.send({
|
|
3141
|
+
* to: 'customer@example.com',
|
|
3142
|
+
* cc: 'billing@your-co.com',
|
|
3143
|
+
* subject: 'Your receipt',
|
|
3144
|
+
* body: '# Thanks!\n\nOrder #1234 is confirmed.',
|
|
3145
|
+
* category: 'transactional',
|
|
3146
|
+
* });
|
|
3147
|
+
*
|
|
3148
|
+
* // A campaign: per-recipient unsubscribe, your own id for joining stats.
|
|
3149
|
+
* const { suppressed, batchId } = await email.send({
|
|
3150
|
+
* to: subscribers,
|
|
3151
|
+
* subject: 'August newsletter',
|
|
3152
|
+
* body: markdown,
|
|
3153
|
+
* batchId: 'newsletter-2026-08',
|
|
3154
|
+
* });
|
|
3155
|
+
* console.log(`skipped ${suppressed.length} unsubscribed`);
|
|
3156
|
+
* const stats = await email.batch(batchId);
|
|
3157
|
+
* ```
|
|
3158
|
+
*/
|
|
3159
|
+
send(options: EmailSendOptions): Promise<EmailSendResult>;
|
|
3160
|
+
/**
|
|
3161
|
+
* Your app's sending allowance and how much is left.
|
|
3162
|
+
*
|
|
3163
|
+
* Useful before a large send: check `remaining` and split the list rather than
|
|
3164
|
+
* discovering the limit as a failure partway through. Going over throws with
|
|
3165
|
+
* `outbound_daily_cap_exceeded`.
|
|
3166
|
+
*/
|
|
3167
|
+
quota(): Promise<EmailQuota>;
|
|
3015
3168
|
/** Counts, rates and a timeseries over a window (default: last 24h). */
|
|
3016
3169
|
stats(options?: EmailWindowOptions & {
|
|
3017
3170
|
buckets?: number;
|
|
@@ -3731,13 +3884,25 @@ declare class MindStudioAgent$1 {
|
|
|
3731
3884
|
*/
|
|
3732
3885
|
get analytics(): Analytics;
|
|
3733
3886
|
/**
|
|
3734
|
-
* Outbound email: the per-recipient delivery log, blast stats, and
|
|
3735
|
-
* unsubscribe list.
|
|
3887
|
+
* Outbound email: sending, the per-recipient delivery log, blast stats, and
|
|
3888
|
+
* this app's unsubscribe list.
|
|
3889
|
+
*
|
|
3890
|
+
* `email.send()` is the send path — the `sendEmail` action still works and runs
|
|
3891
|
+
* the same code, but `send()` returns a receipt including the `batchId` you read
|
|
3892
|
+
* stats back with. Note `category` defaults to `'marketing'`; see
|
|
3893
|
+
* {@link Email.send}.
|
|
3736
3894
|
*
|
|
3737
3895
|
* @example
|
|
3738
3896
|
* ```ts
|
|
3739
3897
|
* import { email } from '@mindstudio-ai/agent';
|
|
3740
3898
|
*
|
|
3899
|
+
* await email.send({
|
|
3900
|
+
* to: 'a@b.com',
|
|
3901
|
+
* subject: 'Your receipt',
|
|
3902
|
+
* body: 'Thanks!',
|
|
3903
|
+
* category: 'transactional',
|
|
3904
|
+
* });
|
|
3905
|
+
*
|
|
3741
3906
|
* // A support lookup: did this person get their mail?
|
|
3742
3907
|
* const { messages } = await email.messages({ recipient: 'a@b.com' });
|
|
3743
3908
|
*
|
|
@@ -11198,11 +11363,15 @@ declare const voice: Voice;
|
|
|
11198
11363
|
*/
|
|
11199
11364
|
declare const analytics: Analytics;
|
|
11200
11365
|
/**
|
|
11201
|
-
* Your app's outbound email — the delivery log, blast stats, and the
|
|
11202
|
-
* list.
|
|
11366
|
+
* Your app's outbound email — sending, the delivery log, blast stats, and the
|
|
11367
|
+
* unsubscribe list.
|
|
11368
|
+
*
|
|
11369
|
+
* `email.send()` is the send path. `mindstudio.sendEmail(...)` still works and
|
|
11370
|
+
* runs the same code underneath, but `send()` returns a proper receipt and sits
|
|
11371
|
+
* next to everything you do with the result.
|
|
11203
11372
|
*
|
|
11204
11373
|
* Scoped to the executing app by its request token, so there is no appId to pass
|
|
11205
|
-
* and no way to read another app
|
|
11374
|
+
* and no way to read or send as another app.
|
|
11206
11375
|
*
|
|
11207
11376
|
* @example
|
|
11208
11377
|
* ```ts
|
package/dist/index.js
CHANGED
|
@@ -2592,6 +2592,7 @@ function toBase64(content) {
|
|
|
2592
2592
|
typeof content === "string" ? Buffer.from(content) : content
|
|
2593
2593
|
).toString("base64");
|
|
2594
2594
|
}
|
|
2595
|
+
var PRESIGN_THRESHOLD = 4 * 1024 * 1024;
|
|
2595
2596
|
function extensionFor(filename) {
|
|
2596
2597
|
if (!filename) {
|
|
2597
2598
|
return "";
|
|
@@ -2619,16 +2620,73 @@ var Store = class {
|
|
|
2619
2620
|
const ext = extensionFor(options?.filename);
|
|
2620
2621
|
const key = options?.key ?? (options?.contentAddressed ? `${createHash("sha256").update(content).digest("hex")}${ext}` : `${randomUUID()}${ext}`);
|
|
2621
2622
|
const cacheControl = options?.cacheControl ?? (!options?.key && this._access === "public" ? "public, max-age=31536000, immutable" : void 0);
|
|
2623
|
+
const bytes = typeof content === "string" ? Buffer.from(content) : content;
|
|
2624
|
+
if (bytes.byteLength >= PRESIGN_THRESHOLD) {
|
|
2625
|
+
return this._putPresigned(bytes, key, {
|
|
2626
|
+
...options?.contentType ? { contentType: options.contentType } : {},
|
|
2627
|
+
...cacheControl ? { cacheControl } : {}
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2622
2630
|
const meta = await this._call("put", {
|
|
2623
2631
|
store: this._store,
|
|
2624
2632
|
access: this._access,
|
|
2625
2633
|
key,
|
|
2626
|
-
body: toBase64(
|
|
2634
|
+
body: toBase64(bytes),
|
|
2627
2635
|
...options?.contentType ? { contentType: options.contentType } : {},
|
|
2628
2636
|
...cacheControl ? { cacheControl } : {}
|
|
2629
2637
|
});
|
|
2630
2638
|
return this._toFile(key, meta);
|
|
2631
2639
|
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Large-content half of {@link put}: mint a presigned POST scoped to exactly
|
|
2642
|
+
* these bytes and send them direct to storage — the JSON transport's body
|
|
2643
|
+
* limit never sees them. Same result contract as the small path; the final
|
|
2644
|
+
* `head` keeps the returned metadata honest (server-observed size/type/time).
|
|
2645
|
+
*/
|
|
2646
|
+
async _putPresigned(bytes, key, options) {
|
|
2647
|
+
const res = await this._call("create-upload", {
|
|
2648
|
+
store: this._store,
|
|
2649
|
+
access: this._access,
|
|
2650
|
+
key,
|
|
2651
|
+
maxSize: bytes.byteLength,
|
|
2652
|
+
...options.contentType ? { contentType: options.contentType } : {},
|
|
2653
|
+
...options.cacheControl ? { cacheControl: options.cacheControl } : {}
|
|
2654
|
+
});
|
|
2655
|
+
const form = new FormData();
|
|
2656
|
+
for (const [k, v] of Object.entries(
|
|
2657
|
+
res.uploadFields
|
|
2658
|
+
)) {
|
|
2659
|
+
form.append(k, v);
|
|
2660
|
+
}
|
|
2661
|
+
const view = new Uint8Array(
|
|
2662
|
+
bytes.buffer,
|
|
2663
|
+
bytes.byteOffset,
|
|
2664
|
+
bytes.byteLength
|
|
2665
|
+
);
|
|
2666
|
+
form.append(
|
|
2667
|
+
"file",
|
|
2668
|
+
new Blob([view], options.contentType ? { type: options.contentType } : void 0)
|
|
2669
|
+
);
|
|
2670
|
+
const upload = await fetch(res.uploadUrl, {
|
|
2671
|
+
method: "POST",
|
|
2672
|
+
body: form
|
|
2673
|
+
});
|
|
2674
|
+
if (!upload.ok) {
|
|
2675
|
+
const errorText = await upload.text().catch(() => "");
|
|
2676
|
+
throw new MindStudioError(
|
|
2677
|
+
`Upload failed: ${upload.status} ${upload.statusText}${errorText ? ` \u2014 ${errorText.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim().slice(0, 200)}` : ""}`,
|
|
2678
|
+
"upload_error",
|
|
2679
|
+
upload.status,
|
|
2680
|
+
errorText || void 0
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
const meta = await this._call("head", {
|
|
2684
|
+
store: this._store,
|
|
2685
|
+
access: this._access,
|
|
2686
|
+
key
|
|
2687
|
+
});
|
|
2688
|
+
return this._toFile(key, meta);
|
|
2689
|
+
}
|
|
2632
2690
|
/** Read an object's bytes (backend / trusted context). */
|
|
2633
2691
|
async get(key) {
|
|
2634
2692
|
const res = await this._call("get", {
|
|
@@ -2968,6 +3026,12 @@ function createAnalytics(call) {
|
|
|
2968
3026
|
var iso = (v) => v === void 0 ? void 0 : v instanceof Date ? v.toISOString() : v;
|
|
2969
3027
|
function createEmail(call) {
|
|
2970
3028
|
return {
|
|
3029
|
+
send(options) {
|
|
3030
|
+
return call("send", options);
|
|
3031
|
+
},
|
|
3032
|
+
quota() {
|
|
3033
|
+
return call("quota", {});
|
|
3034
|
+
},
|
|
2971
3035
|
stats(options) {
|
|
2972
3036
|
return call("stats", {
|
|
2973
3037
|
start: iso(options?.start),
|
|
@@ -6766,13 +6830,25 @@ var MindStudioAgent = class {
|
|
|
6766
6830
|
);
|
|
6767
6831
|
}
|
|
6768
6832
|
/**
|
|
6769
|
-
* Outbound email: the per-recipient delivery log, blast stats, and
|
|
6770
|
-
* unsubscribe list.
|
|
6833
|
+
* Outbound email: sending, the per-recipient delivery log, blast stats, and
|
|
6834
|
+
* this app's unsubscribe list.
|
|
6835
|
+
*
|
|
6836
|
+
* `email.send()` is the send path — the `sendEmail` action still works and runs
|
|
6837
|
+
* the same code, but `send()` returns a receipt including the `batchId` you read
|
|
6838
|
+
* stats back with. Note `category` defaults to `'marketing'`; see
|
|
6839
|
+
* {@link Email.send}.
|
|
6771
6840
|
*
|
|
6772
6841
|
* @example
|
|
6773
6842
|
* ```ts
|
|
6774
6843
|
* import { email } from '@mindstudio-ai/agent';
|
|
6775
6844
|
*
|
|
6845
|
+
* await email.send({
|
|
6846
|
+
* to: 'a@b.com',
|
|
6847
|
+
* subject: 'Your receipt',
|
|
6848
|
+
* body: 'Thanks!',
|
|
6849
|
+
* category: 'transactional',
|
|
6850
|
+
* });
|
|
6851
|
+
*
|
|
6776
6852
|
* // A support lookup: did this person get their mail?
|
|
6777
6853
|
* const { messages } = await email.messages({ recipient: 'a@b.com' });
|
|
6778
6854
|
*
|