@mindstudio-ai/agent 0.1.103 → 0.1.105
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 +649 -32
- package/dist/index.d.ts +532 -66
- package/dist/index.js +607 -26
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +649 -32
- package/llms.txt +74 -19
- package/package.json +1 -1
package/llms.txt
CHANGED
|
@@ -642,23 +642,6 @@ Send data to a Zapier Zap via webhook and return the response.
|
|
|
642
642
|
- Input: `{ webhookUrl: string, input: object }`
|
|
643
643
|
- Output: `{ data: unknown }`
|
|
644
644
|
|
|
645
|
-
#### queryAppDatabase
|
|
646
|
-
Execute a SQL query against the app managed database.
|
|
647
|
-
- Executes raw SQL against a SQLite database managed by the app.
|
|
648
|
-
- For SELECT queries, returns rows as JSON.
|
|
649
|
-
- For INSERT/UPDATE/DELETE, returns the number of affected rows.
|
|
650
|
-
- Use {{variables}} directly in your SQL. By default they are automatically extracted
|
|
651
|
-
and passed as safe parameterized values (preventing SQL injection).
|
|
652
|
-
Example: INSERT INTO contacts (name, comment) VALUES ({{name}}, {{comment}})
|
|
653
|
-
- Full MindStudio handlebars syntax is supported, including helpers like {{json myVar}},
|
|
654
|
-
{{get myVar "$.path"}}, {{global.orgName}}, etc.
|
|
655
|
-
- Set parameterize to false for raw/dynamic SQL where variables are interpolated directly
|
|
656
|
-
into the query string. Use this when another step generates full or partial SQL, e.g.
|
|
657
|
-
a bulk INSERT with a precomputed VALUES list. The user is responsible for sanitization
|
|
658
|
-
when parameterize is false.
|
|
659
|
-
- Input: `{ databaseId: string, sql: string, parameterize?: boolean }`
|
|
660
|
-
- Output: `{ rows: unknown[], changes: number }`
|
|
661
|
-
|
|
662
645
|
#### queryDataSource
|
|
663
646
|
Legacy v1 data sources. v2 apps should use `dataSources.defineDataSource()` from @mindstudio-ai/agent instead, which owns parsing, chunking, embedding and citations. Search a vector data source (RAG) and return relevant document chunks.
|
|
664
647
|
- Queries a vectorized data source and returns the most relevant chunks.
|
|
@@ -830,8 +813,10 @@ Send an email to one or more recipient addresses.
|
|
|
830
813
|
- For threaded replies in a shared inbox, set inReplyTo (the Message-ID being replied to) and references (prior Message-IDs in the thread).
|
|
831
814
|
- connectionId can be a comma-separated list to send to multiple recipients.
|
|
832
815
|
- The special connectionId "trigger_email" uses the email address that triggered the workflow.
|
|
833
|
-
-
|
|
834
|
-
-
|
|
816
|
+
- 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.
|
|
817
|
+
- 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 — use it whenever cc/bcc semantics matter.
|
|
818
|
+
- Input: `{ subject: string, body: string, to?: string | string[], cc?: string | string[], bcc?: string | string[], connectionId?: string, generateHtml?: boolean, generateHtmlInstructions?: string, generateHtmlModelOverride?: { model: string, temperature: number, maxResponseTokens: number, ignorePreamble?: boolean, userMessagePreprocessor?: { dataSource?: string, messageTemplate?: string, maxResults?: number, enabled?: boolean, shouldInherit?: boolean }, preamble?: string, multiModelEnabled?: boolean, editResponseEnabled?: boolean, config?: object }, attachments?: (string | { url: string, filename?: string, contentType?: string })[], from?: string, bodyType?: "auto" | "html" | "markdown" | "text", text?: string, replyTo?: string, inReplyTo?: string, references?: string[], category?: "transactional" | "marketing" }`
|
|
819
|
+
- Output: `{ recipients: string[], cc: string[], bcc: string[], from: string, suppressed: string[] }`
|
|
835
820
|
|
|
836
821
|
#### sendGmailDraft
|
|
837
822
|
Send an existing draft from the connected Gmail account.
|
|
@@ -1854,6 +1839,42 @@ Generated assets: any action that produces a file (`generateImage`, `generateVid
|
|
|
1854
1839
|
const { imageUrl } = await mindstudio.generateImage({ prompt: 'a red sneaker' }, { store: Assets });
|
|
1855
1840
|
```
|
|
1856
1841
|
|
|
1842
|
+
#### `db` — typed tables over the app's managed SQLite
|
|
1843
|
+
|
|
1844
|
+
Chainable, typed collection API over the app's managed databases. Define a table at module scope; every read/write is lazy until awaited, and reads/writes can be batched into one round trip with `db.batch()`.
|
|
1845
|
+
```typescript
|
|
1846
|
+
import { db } from '@mindstudio-ai/agent';
|
|
1847
|
+
|
|
1848
|
+
export const Orders = db.defineTable<Order>('orders');
|
|
1849
|
+
|
|
1850
|
+
// Reads — filters compile to SQL WHERE when possible
|
|
1851
|
+
const recent = await Orders.filter(o => o.status === 'paid').sortBy(o => o.created_at).reverse().take(20);
|
|
1852
|
+
// Comparing to an outer-scope value? Use the bindings form so it compiles to SQL:
|
|
1853
|
+
const mine = await Orders.filter((o, $) => o.requestedBy === $.uid, { uid: auth.userId }); // bindings: lifts closure var so filter compiles to SQL
|
|
1854
|
+
|
|
1855
|
+
// Writes
|
|
1856
|
+
const order = await Orders.push({ item: 'Laptop', amount: 999, status: 'pending' });
|
|
1857
|
+
await Orders.update(order.id, { status: 'approved' });
|
|
1858
|
+
```
|
|
1859
|
+
|
|
1860
|
+
Aggregation — `count()`, `sum(fn)`, `avg(fn)`, `countDistinct(fn)`, and `aggregate(spec)` compile to SQL aggregates (COUNT/TOTAL/AVG/GROUP BY); no rows are fetched, so they stay cheap at any table size. Never page a whole table into memory to compute a summary.
|
|
1861
|
+
```typescript
|
|
1862
|
+
const top = await Answers
|
|
1863
|
+
.filter((a, $) => a.surveyId === $.surveyId, { surveyId }) // bindings: compiles to SQL
|
|
1864
|
+
.aggregate({
|
|
1865
|
+
by: ['questionId', 'dimension'],
|
|
1866
|
+
select: { n: { count: true }, total: { sum: 'score' }, avgScore: { avg: 'score' }, respondents: { countDistinct: 'responseId' } },
|
|
1867
|
+
orderBy: 'total', desc: true, limit: 20,
|
|
1868
|
+
});
|
|
1869
|
+
// Array<{ questionId, dimension, n: number, total: number, avgScore: number | null, respondents: number }>
|
|
1870
|
+
```
|
|
1871
|
+
Select terms: `{ count: true }` · `{ sum: 'col' }` · `{ avg: 'col' }` · `{ min: 'col' }` · `{ max: 'col' }` · `{ countDistinct: 'col' }` (string column names). Omit `by` for whole-set aggregates (single object). Empty set: count/countDistinct 0, sum 0, avg/min/max null.
|
|
1872
|
+
|
|
1873
|
+
Raw SQL escape hatch — `db.sql<T>(query, params?, { database? })` runs read-only raw SQL (joins, subqueries, window functions) against the app's own managed database. SELECT/WITH only; writes throw — use Table methods. Lazy and batchable. Last resort: prefer the typed API (including `aggregate()`) whenever it can express the query; raw rows may not exactly match the typed API's value representations.
|
|
1874
|
+
```typescript
|
|
1875
|
+
const rows = await db.sql<{ questionId: string; n: number }>('SELECT questionId, COUNT(*) AS n FROM answers WHERE surveyId = ? GROUP BY questionId', [surveyId]);
|
|
1876
|
+
```
|
|
1877
|
+
|
|
1857
1878
|
#### `dataSources` — searchable document corpora (RAG)
|
|
1858
1879
|
|
|
1859
1880
|
Declare a data source at module scope and import the handle. The platform owns parsing, chunking, embedding, storage and tenant isolation; every hit returns a citation.
|
|
@@ -1902,3 +1923,37 @@ Adding the same bytes twice is free — documents are content-addressed, so a re
|
|
|
1902
1923
|
Everything in `SearchOptions` — `mode`, `filter`, `rerank`, `hybrid`, `maxPerDocument`, `highlight` — is free to change and takes effect on the next search. How documents are chunked and embedded is configured on the corpus itself with `mindstudio-prod datasources config` — not in code — and changing it on a populated source is rejected rather than silently rebuilding.
|
|
1903
1924
|
|
|
1904
1925
|
**A data source is live and shared.** Unlike database tables there is no dev copy and no per-release isolation: adding or removing a document changes what the deployed app retrieves, immediately. Scenarios never reset a data source, and re-ingesting a large corpus costs real money.
|
|
1926
|
+
|
|
1927
|
+
#### `analytics` — read the app's own traffic + event analytics
|
|
1928
|
+
|
|
1929
|
+
The platform auto-tracks pageviews (and custom events via the frontend's `analytics.track()`); the backend `analytics` namespace is the read side, so app code can correlate real traffic with its own data — lifetime views next to each blog post, "N people reading now", conversions beside the records that produced them. Backend only (hook token); reads the LIVE app's real traffic even from a dev sandbox. Rate limited per app (120 reads/min).
|
|
1930
|
+
|
|
1931
|
+
One general read, `query()`, shaped like the Plausible/GA4 query APIs:
|
|
1932
|
+
|
|
1933
|
+
```typescript
|
|
1934
|
+
import { analytics } from '@mindstudio-ai/agent';
|
|
1935
|
+
|
|
1936
|
+
// Lifetime pageviews + unique visitors per page — top 10.
|
|
1937
|
+
const top = await analytics.query({
|
|
1938
|
+
metrics: ['pageviews', 'visitors'],
|
|
1939
|
+
dimensions: ['path'],
|
|
1940
|
+
dateRange: 'all',
|
|
1941
|
+
limit: 10,
|
|
1942
|
+
});
|
|
1943
|
+
// top.results[0] → { dimensions: { path: '/post/hello' }, metrics: { pageviews: 4210, visitors: 1830 } }
|
|
1944
|
+
|
|
1945
|
+
// Daily views for one post, full history.
|
|
1946
|
+
const series = await analytics.query({
|
|
1947
|
+
metrics: ['pageviews'],
|
|
1948
|
+
dimensions: ['time'],
|
|
1949
|
+
granularity: 'day',
|
|
1950
|
+
filters: [['is', 'path', ['/post/hello']]],
|
|
1951
|
+
dateRange: 'all',
|
|
1952
|
+
});
|
|
1953
|
+
```
|
|
1954
|
+
|
|
1955
|
+
Grammar: `metrics` from `pageviews | visitors | visits | events`; `dimensions` is at most ONE entity dimension (`path`, `referrerHost`, `sourceCategory`, `country`, `city`, `deviceType`, `browser`, `os`, `language`, `visitorType`, `utmSource`…`utmContent`, `eventName`) OR `'time'` (with `granularity: '5m'|'hour'|'day'|'week'|'month'` and optional IANA `timezone`); `filters` are `[op, dimension, values]` triples with ops `is | is_not | contains`; `dateRange` is a shorthand (`'1h'|'24h'|'7d'|'30d'|'90d'|'all'`) or `[startISO, endISO]`; `orderBy` (grouped only) accepts `pageviews` or `events`. With `dateRange: 'all'`, granularity is a minimum — the server coarsens as history grows. `city` filter values are plain city names (any country) and always read the 90-day event detail.
|
|
1956
|
+
|
|
1957
|
+
**Retention split — the one thing to get right:** queries whose filters are all `is` and touch at most one dimension read a rollup kept forever (full lifetime history, exact unique visitors). Cross-dimension intersections, `is_not`, and `contains` scan raw events retained for 90 days; the server clamps the window and reports it. Check `meta`: `source` (`rollup` vs `events`), `clamped`, `window.served`, and `metricsOmitted` — metrics a shape can't carry (e.g. `visits` on grouped reads) are omitted there, never returned as silent zeros. `meta.total` carries the unpaginated group count for paging.
|
|
1958
|
+
|
|
1959
|
+
Specials (same `dateRange`/filter-triple grammar): `analytics.live()` → `{count, countries, sparkline}` right now; `analytics.sources(opts?)` → per-session first-source ranking (UTM > referrer > direct) classified by category/vendor incl. AI assistants; `analytics.map(opts?)` → city lat/lon points; `analytics.aiSources(opts?)` → AI-assistant referrals per vendor; `analytics.crawlers.overview()/timeseries()/recent()` → bot/AI-crawler ingestion of the app's pages.
|