@mindstudio-ai/remy 0.1.263 → 0.1.264
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.
|
@@ -30,7 +30,9 @@ short docs that fit in a prompt.
|
|
|
30
30
|
- **Limits apply**: 25 data sources per app, 5,000 documents per source, 10,000 chunks per
|
|
31
31
|
document, 300 searches/minute. Well clear of normal use — but **source names must be fixed, not
|
|
32
32
|
computed per user or per request**, since referencing one creates it. Partition inside a source
|
|
33
|
-
with
|
|
33
|
+
with document metadata instead: tag at add time
|
|
34
|
+
(`add(bytes, { filename, metadata: { userId } })`), narrow at search time
|
|
35
|
+
(`search(q, { filter: { metadata: { userId } } })`).
|
|
34
36
|
|
|
35
37
|
## Defining and searching
|
|
36
38
|
|
|
@@ -51,7 +53,22 @@ beside the answer. Retrieval is approximate; a user who can click through can ju
|
|
|
51
53
|
An answer with no citation is an assertion.
|
|
52
54
|
|
|
53
55
|
Created on first use, so searching a source the build hasn't populated returns no results rather than
|
|
54
|
-
throwing. `search` options: `topK` (default 5, max 50), `scoreThreshold`, `
|
|
56
|
+
throwing. `search` options: `topK` (default 5, max 50), `scoreThreshold`, `filter`, `mode`,
|
|
57
|
+
`maxPerDocument`, `highlight`, `rerank`, `hybrid`.
|
|
58
|
+
|
|
59
|
+
**Filtering** narrows a search before ranking, and every condition only narrows:
|
|
60
|
+
`filter: { metadata: { department: 'legal', year: [2025, 2026] }, filename, documentIds,
|
|
61
|
+
pages: { min?, max? }, contains: 'all these words', phrase: 'exact adjacent sequence' }`.
|
|
62
|
+
Metadata is tagged at add time (scalars only, ≤16 keys); re-adding the same bytes with different
|
|
63
|
+
metadata updates the tags in place, free. Filters are the right tool for scoping retrieval
|
|
64
|
+
(per-user, per-category); they are NOT a substitute for a `db` query over structured data.
|
|
65
|
+
|
|
66
|
+
**Modes**: `mode: 'hybrid'` (default) fuses semantic and keyword retrieval; `'semantic'` is the
|
|
67
|
+
embedding alone; `'lexical'` is keyword-only with **no query embedding** — cheapest and fastest,
|
|
68
|
+
right when the query is an identifier (an error code, a SKU, a name) rather than a meaning.
|
|
69
|
+
`maxPerDocument: 2` stops one document monopolizing the results when the answer should draw on
|
|
70
|
+
several. `highlight: true` adds `matches` (`{start, end}` offsets into `text`) for rendering
|
|
71
|
+
highlighted excerpts.
|
|
55
72
|
|
|
56
73
|
Search is deterministic for a fixed corpus and configuration, so eval sets and regression checks are
|
|
57
74
|
meaningful — key them on `(documentId, chunkIndex)` rather than on chunk text.
|
|
@@ -69,7 +86,9 @@ set with the CLI, so code and reality can't drift.
|
|
|
69
86
|
|
|
70
87
|
```bash
|
|
71
88
|
mindstudio-prod datasources add --source policies --wait docs/*.pdf
|
|
89
|
+
mindstudio-prod datasources add --source policies --metadata department=legal,year=2026 contract.pdf
|
|
72
90
|
mindstudio-prod datasources search --source policies "what are the payment terms?" # sanity-check
|
|
91
|
+
mindstudio-prod datasources search --source policies --filter department=legal --mode lexical "ERR-7741X"
|
|
73
92
|
mindstudio-prod datasources delete --source policies # whole source; --source is required, never defaulted
|
|
74
93
|
```
|
|
75
94
|
|
|
@@ -83,7 +102,11 @@ it needs no guard.
|
|
|
83
102
|
Use the SDK's `add()` only when *users* upload documents that must become searchable:
|
|
84
103
|
|
|
85
104
|
```typescript
|
|
86
|
-
await Policies.add(buffer, {
|
|
105
|
+
await Policies.add(buffer, {
|
|
106
|
+
filename: 'policy.pdf',
|
|
107
|
+
contentType: 'application/pdf',
|
|
108
|
+
metadata: { department: 'legal' }, // filterable at search time
|
|
109
|
+
});
|
|
87
110
|
const docs = await Policies.documents(); // 'processing' | 'done' | 'error'
|
|
88
111
|
await Policies.remove(documentId);
|
|
89
112
|
```
|
|
@@ -30,20 +30,35 @@ everywhere else, and the compiled system prompt must carry them explicitly:
|
|
|
30
30
|
- **Spoken-form values.** "Forty-two fifty," not "$42.50". "Two fifteen in the afternoon," not
|
|
31
31
|
"14:15". Read email addresses and confirmation codes character by character, and read them *back*
|
|
32
32
|
for confirmation before acting on them — mishearing one digit of a phone number is the classic
|
|
33
|
-
voice failure.
|
|
33
|
+
voice failure. Collect one value per turn; two asked together blend when spoken.
|
|
34
34
|
- **Brevity is a hard rule, not a style preference.** One to two sentences per turn, one question at
|
|
35
35
|
a time. A paragraph that reads fine in chat is a monologue on a call.
|
|
36
|
+
- **Vary the phrasing.** Repeated openers and acknowledgments sound convincing once and robotic by
|
|
37
|
+
the third turn — give the prompt an explicit variety rule, and treat any sample phrases as
|
|
38
|
+
anchors, never scripts.
|
|
36
39
|
- **Handle unclear audio explicitly.** Give the prompt a rule for it: respond only to clear audio;
|
|
37
|
-
if it's noisy or ambiguous, ask the user to repeat — never guess,
|
|
38
|
-
|
|
40
|
+
if it's noisy or ambiguous, ask the user to repeat — never guess, never call a tool on input the
|
|
41
|
+
agent isn't sure it heard, and don't reuse the same clarification line twice in a row.
|
|
39
42
|
- **Pin the language.** State the response language in the prompt; don't let the model infer it from
|
|
40
|
-
an accent.
|
|
43
|
+
an accent. If the app's domain has brand names or terms with non-obvious pronunciations, give
|
|
44
|
+
them a line ("pronounce SQL as 'sequel'").
|
|
41
45
|
|
|
42
46
|
Beyond the mechanics, the persona itself should be *of the ear*: pacing, warmth, how it handles
|
|
43
47
|
being interrupted, what it says when it needs a second. This is the fun part, same as the agent
|
|
44
48
|
interface — a distinct character beats a generic assistant, and voice makes character land harder
|
|
45
49
|
than any other surface.
|
|
46
50
|
|
|
51
|
+
### The shape of `system.md`
|
|
52
|
+
|
|
53
|
+
Structure the compiled prompt as short **labeled sections** — Role & Objective, Personality & Tone,
|
|
54
|
+
Rules, and (when the app has a real call flow) Conversation Flow — with bullets over paragraphs;
|
|
55
|
+
realtime models find and follow sectioned rules far more reliably than prose. Scope rules
|
|
56
|
+
precisely: "confirm before any tool that changes data," not "always confirm everything" — blanket
|
|
57
|
+
`always`/`never` makes the agent rigid and unable to handle reasonable exceptions. And start
|
|
58
|
+
minimal: state the role, the boundaries, and the voice mechanics above, then add rules only for
|
|
59
|
+
behaviors that actually misfire in test calls (the transcripts in the call log are the feedback
|
|
60
|
+
loop) rather than front-loading a policy manual.
|
|
61
|
+
|
|
47
62
|
### The latency classes
|
|
48
63
|
|
|
49
64
|
Every tool in the spec declares one of three classes. This is the voice-specific discipline — get it
|
|
@@ -83,6 +98,11 @@ about to happen and get a yes. Anything destructive or financial — read the de
|
|
|
83
98
|
piece by piece. In voice there is no confirmation dialog to lean on; the conversation *is* the
|
|
84
99
|
confirmation UI.
|
|
85
100
|
|
|
101
|
+
And give failure a script: never speak a raw error. When a lookup misses or a tool fails, read back
|
|
102
|
+
the value it used ("I couldn't find an order ending three-one-two-five — did I get part of that
|
|
103
|
+
wrong?"), offer one retry, then move to an alternate path — in character, without blaming the
|
|
104
|
+
caller.
|
|
105
|
+
|
|
86
106
|
### Choosing the model
|
|
87
107
|
|
|
88
108
|
Two shapes, one `model` field:
|
|
@@ -210,6 +230,116 @@ app's voice ("Booking your appointment…"), never raw names or JSON.
|
|
|
210
230
|
- Rendering user-side captions as authoritative ("you said X") — they're recognition output.
|
|
211
231
|
- Auto-starting a session on page load. Microphone access is always a deliberate user action.
|
|
212
232
|
|
|
233
|
+
## Outbound calls (`voice.call`)
|
|
234
|
+
|
|
235
|
+
The agent can call the user. Backend methods (and crons) place outbound phone calls with the
|
|
236
|
+
agent SDK's `voice` namespace — the platform dials the number and connects the callee to this
|
|
237
|
+
app's voice agent (same persona, engine, and tools as the web sessions):
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { voice, auth } from '@mindstudio-ai/agent';
|
|
241
|
+
|
|
242
|
+
export async function callMeAboutMyOrder(input: { phone: string }) {
|
|
243
|
+
auth.requireRole('member');
|
|
244
|
+
const call = await voice.call({ to: input.phone, assumeIdentity: true });
|
|
245
|
+
return { calling: call.to, from: call.from };
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- **The method is the authorization gate.** The voice interface's `auth` block does not apply to
|
|
250
|
+
calls the backend places deliberately — gate the *method* with `auth.requireRole(...)` exactly
|
|
251
|
+
as you would any sensitive action.
|
|
252
|
+
- **`assumeIdentity: true`** runs the call as the user who invoked the method: the agent knows
|
|
253
|
+
who it's talking to (Current User block) and every tool call carries their roles — regardless
|
|
254
|
+
of which number was dialed (the user types any number into a field; identity comes from their
|
|
255
|
+
session, not the phone). Omitted/false → anonymous call; role-gated tools decline.
|
|
256
|
+
System/cron invocations have no human identity and always run anonymously.
|
|
257
|
+
- **Production needs a dedicated phone number.** The app owner attaches one ($2/month) via the
|
|
258
|
+
dashboard or `mindstudio-prod voice numbers` (see "Managing the phone side from the CLI"
|
|
259
|
+
below) — it becomes the caller ID for every call, in dev sessions too, so users always see
|
|
260
|
+
the same number. Without one, deployed calls throw `phone_out_requires_dedicated_number`, and
|
|
261
|
+
dev sessions fall back to a shared platform test number that varies per call (tighter limits
|
|
262
|
+
apply on the shared pool).
|
|
263
|
+
- **Outcome is on the call record**, not the return value: `voice.call` returns as soon as
|
|
264
|
+
dialing starts (`{ sessionId, status: 'dialing', from, to }`); answered/busy/no-answer land on
|
|
265
|
+
the session in the app's call log (`voice.listSessions()` / the dashboard).
|
|
266
|
+
- **Limits**: the app's concurrent-session policy, a daily outbound-call cap, a per-call
|
|
267
|
+
duration ceiling, and one active call per callee number (`voice_callee_busy`).
|
|
268
|
+
- **Compliance**: automated calls require prior consent. Call your own users who opted in to
|
|
269
|
+
calls from this app, honor reasonable calling hours, never dial purchased or cold lists —
|
|
270
|
+
design the consent moment into the product (a "call me" button IS consent; a scraped list is
|
|
271
|
+
not).
|
|
272
|
+
|
|
273
|
+
## Inbound calls
|
|
274
|
+
|
|
275
|
+
Once the app has a dedicated phone number, people can call it — the same voice agent answers
|
|
276
|
+
(same persona, engine, and tools). Nothing extra to author for the basic case; the number in the
|
|
277
|
+
app's settings is the whole switch.
|
|
278
|
+
|
|
279
|
+
How answering works:
|
|
280
|
+
|
|
281
|
+
- **Inbound always runs the live release.** There is no dev inbound — test the agent over the
|
|
282
|
+
normal WebRTC session in the editor; the phone is the same interface with a different
|
|
283
|
+
transport. An app with no live voice interface (or at its concurrency limit) doesn't answer.
|
|
284
|
+
- **Callers are anonymous until verified.** The `auth` block still applies, but a phone call
|
|
285
|
+
can't show a login page — so the platform answers first, and `requireUser` becomes an
|
|
286
|
+
in-call verification flow. The agent can serve whatever anonymous callers are allowed, and
|
|
287
|
+
offers verification when the caller wants something account-bound.
|
|
288
|
+
- **Verification uses the app's own auth methods** (`sms-code` / `email-code` from the
|
|
289
|
+
manifest), existing accounts only — there is no sign-up over the phone:
|
|
290
|
+
- SMS: a code is texted to the number the caller is calling from, if an account has that
|
|
291
|
+
number on file. No other number is possible by design.
|
|
292
|
+
- Email: the caller says their address; the platform matches it against the app's users
|
|
293
|
+
(transcription-tolerant — no letter-by-letter spelling ceremony) and emails the account's
|
|
294
|
+
stored address a code.
|
|
295
|
+
- The flow never confirms or denies that an account exists — a code is "sent if an account
|
|
296
|
+
matches", always phrased that neutrally. The persona should offer verification naturally
|
|
297
|
+
when it unlocks something, never as a robotic gate.
|
|
298
|
+
- **Verified mid-call, upgraded mid-call**: once the code checks out, the session becomes that
|
|
299
|
+
user's — Current User block, roles on every tool call — without redialing.
|
|
300
|
+
|
|
301
|
+
### `phone.trustCallerId`
|
|
302
|
+
|
|
303
|
+
For apps whose users are known by phone number, the interface config may opt into treating
|
|
304
|
+
caller ID as identity:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
"phone": { "trustCallerId": true }
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
A caller whose number exactly matches an app user's phone starts the call already verified —
|
|
311
|
+
no code. This is a real security tradeoff: **caller ID can be spoofed**, so a motivated
|
|
312
|
+
attacker who knows a user's phone number can impersonate them to this agent. Before enabling
|
|
313
|
+
it, you MUST surface that risk to the user and get their explicit confirmation — it's the
|
|
314
|
+
right call for convenience-first, low-stakes apps (a family assistant, a status line), and the
|
|
315
|
+
wrong one wherever the agent's tools can move money, reveal sensitive records, or take
|
|
316
|
+
destructive actions. It lives in the interface config deliberately: enabling it is a code
|
|
317
|
+
change, visible in review and auditable via deploys, not a dashboard toggle.
|
|
318
|
+
|
|
319
|
+
## Managing the phone side from the CLI
|
|
320
|
+
|
|
321
|
+
The `mindstudio-prod voice` family covers numbers, the call log, and voice policy:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
mindstudio-prod voice numbers search --area-code 310 # available numbers to offer the user
|
|
325
|
+
mindstudio-prod voice numbers buy +13105551234 # buy + attach ($2/month — see below)
|
|
326
|
+
mindstudio-prod voice numbers release +13105551234 # permanent; no refund, ~15-day quarantine
|
|
327
|
+
mindstudio-prod voice sessions list --limit 10 # call log: web / phone-out / phone-in
|
|
328
|
+
mindstudio-prod voice sessions get <sessionId> # full transcript + cost breakdown
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Also `voice numbers list`, `voice settings get`/`set` (concurrency, per-visitor,
|
|
332
|
+
max duration). `--help` for flags.
|
|
333
|
+
|
|
334
|
+
**Never buy a number without the user's explicit confirmation** — it starts a recurring
|
|
335
|
+
$2/month workspace charge. Search first, present the options with the price, and only run
|
|
336
|
+
`numbers buy` after they've picked one and said yes.
|
|
337
|
+
|
|
338
|
+
Transcripts are how you iterate on a voice persona: after the user test-calls the agent, read
|
|
339
|
+
`voice sessions get` for what was actually said — misheard input, interruptions, tools declining
|
|
340
|
+
— and fix the spec from evidence rather than guesses. (Dev-session test calls carry a
|
|
341
|
+
`devSessionId` in the list, so you can tell them from live traffic.)
|
|
342
|
+
|
|
213
343
|
---
|
|
214
344
|
|
|
215
345
|
# The wiring
|
|
@@ -307,6 +437,8 @@ The top-level key must match the interface type (`voice`):
|
|
|
307
437
|
| `greeting` | Optional spoken opener |
|
|
308
438
|
| `systemPrompt` | Relative path to the compiled system prompt |
|
|
309
439
|
| `auth` | **Required.** Who may start a session: `{ "requireUser": boolean, "requireRole"?: string[] }`. See the Auth section below |
|
|
440
|
+
| `phone` | Optional telephony options: `{ "trustCallerId"?: boolean }` — see "Inbound calls" above. Only add it after the user has confirmed the spoofing tradeoff |
|
|
441
|
+
| `context` | Optional session context: `{ "method": <method id> }` — auto-fired in the background at session start; see "Session context" below |
|
|
310
442
|
| `tools` | `{ method, latency, description }` — method `id` from the manifest, a latency class, and a relative path to the tool's markdown |
|
|
311
443
|
| `webInterfacePath` | Optional. Where the voice layer lives in the web interface, for the editor preview |
|
|
312
444
|
|
|
@@ -316,6 +448,30 @@ Declare it in `mindstudio.json`:
|
|
|
316
448
|
{ "type": "voice", "path": "dist/interfaces/voice/interface.json" }
|
|
317
449
|
```
|
|
318
450
|
|
|
451
|
+
## Session context (auto-loaded)
|
|
452
|
+
|
|
453
|
+
When the config declares `"context": { "method": "session-context" }`, the platform fires that
|
|
454
|
+
backend method automatically when a session starts — in the background, so the greeting is
|
|
455
|
+
never delayed — and appends its return to the system prompt as a `## Session Context` block.
|
|
456
|
+
Use it for situational state that should color every turn: the caller's open orders, account
|
|
457
|
+
standing, where they left off. Timing: the method runs while the greeting audio plays, so the
|
|
458
|
+
agent has the context by roughly the first exchange and is guaranteed to have it shortly
|
|
459
|
+
after — it is NOT guaranteed for the literal first utterance. On inbound phone calls it
|
|
460
|
+
re-fires after the caller verifies mid-call, so the context recomputes for the now-known user.
|
|
461
|
+
|
|
462
|
+
The method contract:
|
|
463
|
+
- Runs as the session's user (same identity/RBAC as a tool call); anonymous sessions run it
|
|
464
|
+
anonymously — return generic or empty content for them.
|
|
465
|
+
- Return a short markdown **string** (a few lines). Results are capped at 4,000 characters;
|
|
466
|
+
keep it situational context, not documents — deep or on-demand data belongs in tools or
|
|
467
|
+
data sources.
|
|
468
|
+
- It is never a model-visible tool, and failures degrade silently to the generic prompt —
|
|
469
|
+
never make correctness depend on it.
|
|
470
|
+
|
|
471
|
+
Rule of thumb: `context` for always-relevant state the agent should just know; tools for
|
|
472
|
+
anything looked up on demand. Identity itself (name, roles) is already injected via the
|
|
473
|
+
Current User block — don't re-fetch it in the context method.
|
|
474
|
+
|
|
319
475
|
## Platform Behavior
|
|
320
476
|
|
|
321
477
|
- Input schemas are derived from each method's contract — never hand-written.
|
|
@@ -347,6 +503,8 @@ creation itself:
|
|
|
347
503
|
(OR semantics, same as the backend `auth.requireRole(...)`). Omit or leave empty for no role
|
|
348
504
|
gate. Requires `requireUser: true`. Unknown role ids fail the build.
|
|
349
505
|
- Denials reject `startSession()` with code `auth_required` (401) or `role_required` (403).
|
|
506
|
+
- On the phone channel there is no login page to bounce to, so `requireUser` becomes
|
|
507
|
+
answer-then-verify — see "Inbound calls" above.
|
|
350
508
|
- Dev preview is exempt — the builder is never locked out while testing.
|
|
351
509
|
- Older compiled apps without the block fall back to the manifest's `auth.enabled` (auth-enabled →
|
|
352
510
|
users only; no auth → public). New configs always declare it explicitly.
|
|
@@ -81,6 +81,6 @@ You have access to the `mindstudio` CLI, which exposes every SDK action as a com
|
|
|
81
81
|
### Production App Management
|
|
82
82
|
You have access to `mindstudio-prod`, a CLI for managing the user's production app. Use it via your bash tool. All output is JSON. Run `mindstudio-prod --help` or `mindstudio-prod <command> --help` to discover usage and available options.
|
|
83
83
|
|
|
84
|
-
Available commands: `requests` (server-side request logs, error rates, latency), `crashes` (frontend browser errors — grouped issues + drill-down to individual events), `analytics` (traffic, top pages/referrers/geo, AI-referral attribution, live counters), `releases` (deploy status, history), `domains` (custom subdomains and fully custom domains), `users` (list, set roles), `db` (query production sql db), `data` (live db operations like lift-from-dev), `methods` (list, invoke), `secrets` (list, get, set, delete), `issues` (reference and manage externally-reported bugs and issues. Do not use this to track work you are doing with the user - only read from it and resolve issues if the user asks for help fixing a bug from the issues tracker).
|
|
84
|
+
Available commands: `requests` (server-side request logs, error rates, latency), `crashes` (frontend browser errors — grouped issues + drill-down to individual events), `analytics` (traffic, top pages/referrers/geo, AI-referral attribution, live counters), `releases` (deploy status, history), `diagnostics` (post-deploy Lighthouse audit), `domains` (custom subdomains and fully custom domains), `users` (list, set roles), `db` (query production sql db), `data` (live db operations like lift-from-dev), `methods` (list, invoke), `secrets` (list, get, set, delete), `files` (upload + manage CDN files), `datasources` (build and query document corpora), `prerender` (bot/crawler snapshots), `voice` (dedicated phone numbers: search/buy/release — buying bills $2/month, so never buy without the user's explicit confirmation; call log + transcripts; voice policy settings), `issues` (reference and manage externally-reported bugs and issues. Do not use this to track work you are doing with the user - only read from it and resolve issues if the user asks for help fixing a bug from the issues tracker).
|
|
85
85
|
|
|
86
86
|
Use when the user asks about production behavior (server errors via `requests`, browser crashes via `crashes`, traffic/engagement via `analytics`), wants to manage their live app (domains, users, roles), needs to seed or query production data, or wants to check release status.
|