@mindstudio-ai/remy 0.1.304 → 0.1.306
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.
|
@@ -46,6 +46,7 @@ All fields are nested under the `"web"` key.
|
|
|
46
46
|
| `prerender` | `object` | — | Opt into prerendering the listed routes/patterns for crawlers/unfurlers. See "Prerendering" below. |
|
|
47
47
|
| `mounts` | `array` | — | Serve other same-workspace apps under path prefixes of this app's hosts. See "Mounting other apps" below. |
|
|
48
48
|
| `redirects` | `array` | — | Path-level redirects. See "Redirects" below. |
|
|
49
|
+
| `rewrites` | `array` | — | Serve different content at the same URL. See "Rewrites" below. |
|
|
49
50
|
| `trailingSlash` | `"strip"` \| `"append"` | — | Enforce a canonical trailing-slash form with a 308. Off by default. |
|
|
50
51
|
|
|
51
52
|
### Frontend SDK
|
|
@@ -168,11 +169,26 @@ Declare moved URLs in `web.json` — never ship a component that redirects clien
|
|
|
168
169
|
|
|
169
170
|
`trailingSlash` picks the canonical form (`"strip"`: `/about/` → `/about`; `"append"`: the reverse, skipping the root and paths with file extensions). Off by default. Write `source` patterns without trailing slashes either way — normalization and matching resolve in one hop. Sources under `/_/` are rejected, as are self-referential and two-rule loops. A redirecting path is never prerendered, so don't list one in `prerender.paths`.
|
|
170
171
|
|
|
172
|
+
### Rewrites
|
|
173
|
+
|
|
174
|
+
Serve different content at the same URL — no redirect. The way to give a clean path to a file that isn't in the build output:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{ "web": { "rewrites": [
|
|
178
|
+
{ "source": "/sitemap.xml", "destination": "/_/files/public/site/sitemap.xml" },
|
|
179
|
+
{ "source": "/docs/*rest", "destination": "/help/*rest" }
|
|
180
|
+
] } }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`destination` is either `/_/files/public/<store>/<key>` (the object's bytes served inline — use this for anything the app generates at runtime and uploads: sitemaps, `robots.txt`, `llms.txt`, `.well-known/*`; the store must be public, private is rejected at build) or another path in the build output. Same `source` syntax as redirects, first match wins. Objects over 10 MB fall back to a CDN redirect. External URLs are not supported (use `mounts`), nor are other `/_/` surfaces.
|
|
184
|
+
|
|
185
|
+
Redirects resolve before everything; rewrites resolve at the content lookup, after prerendering — so a path with both gets the redirect.
|
|
186
|
+
|
|
171
187
|
### Mounting other apps
|
|
172
188
|
|
|
173
189
|
Rare: `mounts` serves another same-workspace app under a path prefix of this app's hosts — `{ "web": { "mounts": [{ "path": "/docs", "app": "docs-site" }] } }` (`app` = the target's `custom_subdomain` or appId). The child is served first-class (its own bundle, session, backend, prerendering) and needs no mount-specific config; the two serving conventions above are what make an app mountable.
|
|
174
190
|
|
|
175
|
-
Under a mount the child's own `web.json` governs its
|
|
191
|
+
Under a mount the child's own `web.json` governs its routing: its `prerender.paths` gates its mounted pages, its `prerender.invalidate` purges them, and its `redirects`/`rewrites`/`trailingSlash` apply within the prefix (written against its own paths). SEO files are NOT handled for you — the child must generate its sitemap with the mounted URLs, and the parent must advertise it in its own `robots.txt` or sitemap index. Canonicals must be written from the page's own location; a hardcoded absolute canonical points crawlers back at the child's host and undoes the mount.
|
|
176
192
|
|
|
177
193
|
## API Interface
|
|
178
194
|
|
|
@@ -112,17 +112,29 @@ await chat.deleteThread(thread.id);
|
|
|
112
112
|
await chat.claimThread(thread.id);
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
**Client tools** — a tool whose effect happens in the browser (open a sheet,
|
|
115
|
+
**Client tools** — a tool whose effect happens in the browser (open a sheet, pick a file, confirm an action) is declared with `target: "client"` and a `name` + inline `inputSchema` instead of a `method` (names must not collide with method ids; the schema is authored — there's no method contract to derive it from). Register a handler and its **return value becomes the tool result**, so the agent learns what happened rather than assuming it did:
|
|
116
116
|
|
|
117
117
|
```js
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (name === 'showVerification') openVerifySheet(input);
|
|
122
|
-
},
|
|
118
|
+
chat.registerClientTool('pickFile', async ({ prompt }) => {
|
|
119
|
+
const file = await openFilePicker(prompt);
|
|
120
|
+
return file ? { path: file.path } : { cancelled: true };
|
|
123
121
|
});
|
|
122
|
+
|
|
123
|
+
// Holding the turn on a person: the handler resolves when they decide.
|
|
124
|
+
chat.registerClientTool(
|
|
125
|
+
'confirmDeploy',
|
|
126
|
+
({ summary }) =>
|
|
127
|
+
new Promise((resolve) => {
|
|
128
|
+
showApprovalDialog(summary, {
|
|
129
|
+
onApprove: (note) => resolve({ approved: true, note }),
|
|
130
|
+
onReject: (reason) => resolve({ approved: false, reason }),
|
|
131
|
+
});
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
124
134
|
```
|
|
125
135
|
|
|
136
|
+
The agent waits while the handler runs, up to 15 minutes — which is what makes confirm-before-acting a client tool rather than something you build a queue for. The SDK always answers, so the agent is never stuck: the return value, `{ error }` if the handler threw, `result_too_large` past ~32KB serialized, and `unhandled_client_tool` immediately when nothing is registered for that name; the platform supplies `client_timeout` if the window passes and `client_disconnected` if the page closes. Handlers live for the client's lifetime rather than one message. `onClientToolCall` on `sendMessage` behaves the same way for a one-off — whatever it returns is the result — and is consulted only when no handler is registered.
|
|
137
|
+
|
|
126
138
|
**Sending messages (streaming):**
|
|
127
139
|
|
|
128
140
|
`sendMessage` streams the agent's response via SSE. Use named callbacks for common events:
|
|
@@ -39,7 +39,7 @@ These are things we already know about and have decided to accept:
|
|
|
39
39
|
- use [wouter](https://github.com/molefrog/wouter) for React routing instead of reaching for react-router
|
|
40
40
|
- uploading user files should always happen via `platform.uploadFile()` from `@mindstudio-ai/interface` — not custom S3 code, not FormData to a method endpoint
|
|
41
41
|
- for build-time prerendering of purely static sites (marketing pages with no dynamic content — distinct from the platform's crawler prerendering, below), roll your own with a post-build `renderToString` script — do not use `vite-prerender-plugin` (it bundles the prerender script as a client chunk, adding ~800KB to the user-facing bundle with no way to prevent it)
|
|
42
|
-
- **Prerendering for crawlers/unfurlers is a platform feature — don't design around it.** For SEO / link-unfurl / AI-crawler visibility on a non-static SPA, the platform already handles it: routes opt in via `web.json` (`{ "web": { "prerender": { "paths": ["/blog/*"] } } }`), the SPA signals readiness by setting `data-prerender-ready` on the html element, deploys invalidate the snapshot cache automatically, and content that changes outside deploys is invalidated at runtime with `await prerender.invalidate([...])` from `@mindstudio-ai/agent` (called from the mutating method). It serves cached headless snapshots of the live SPA to bots — it is NOT build-time rendering. Do not recommend post-build render scripts, rebuild-on-content-change, or third-party prerender services for this. An app mounted under another app's path prefix keeps its own `prerender.paths` and its own `prerender.invalidate` — don't recommend moving prerender config to the parent
|
|
42
|
+
- **Prerendering for crawlers/unfurlers is a platform feature — don't design around it.** For SEO / link-unfurl / AI-crawler visibility on a non-static SPA, the platform already handles it: routes opt in via `web.json` (`{ "web": { "prerender": { "paths": ["/blog/*"] } } }`), the SPA signals readiness by setting `data-prerender-ready` on the html element, deploys invalidate the snapshot cache automatically, and content that changes outside deploys is invalidated at runtime with `await prerender.invalidate([...])` from `@mindstudio-ai/agent` (called from the mutating method). It serves cached headless snapshots of the live SPA to bots — it is NOT build-time rendering. Do not recommend post-build render scripts, rebuild-on-content-change, or third-party prerender services for this. An app mounted under another app's path prefix keeps its own `prerender.paths` and its own `prerender.invalidate` — don't recommend moving prerender config to the parent. SEO files under a mount are NOT automatic: the child generates its sitemap with the mounted URLs (point its base-URL config at the mount) and the parent advertises it in its own robots.txt or sitemap index. To give a generated file a clean path (`/sitemap.xml`, `/robots.txt`, `/llms.txt`, `.well-known/*`) use a `web.json` rewrite to `/_/files/public/<store>/<key>` — never a redirect, which moves the URL, and never a client-side component. The developer's main context carries the full interfaces reference with exact semantics — tell them to consult it rather than improvising.
|
|
43
43
|
|
|
44
44
|
### Common pitfalls (always flag these)
|
|
45
45
|
|