@mindstudio-ai/remy 0.1.277 → 0.1.278

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.
@@ -202,25 +202,23 @@ export async function createPurchaseOrder(input: {
202
202
 
203
203
  A method can return immediately while kicking off slow work (like `runTask()`) that continues in the background. Don't await the slow call — use `.then()` / `.catch()` to update the record when it completes, and return an early result to the caller. The frontend polls the record's status to track progress. Wrap background chains other than `runTask()` in `mindstudio.waitUntil(...)` so the platform keeps the sandbox alive for them and records an interruption if they're cut short — `runTask()` registers itself automatically.
204
204
 
205
- The example below shows the fire-and-forget shape, not a complete `runTask()` call. Load the `taskAgents` skill before writing one — configuring its tools, validating the output, and handling failures are all there, and none of them are visible here.
205
+ The example below shows the fire-and-forget shape, not a complete `runTask()` call. Load the `taskAgents` skill before writing one — configuring its tools, the `outputSchema` output contract, and handling failures are all there, and none of them are visible here.
206
206
 
207
207
  ```typescript
208
208
  export async function enrichRestaurant(input: { id: string; name: string }) {
209
209
  await Restaurants.update(input.id, { status: 'enriching' });
210
210
 
211
211
  // Fire — don't await
212
- mindstudio.runTask<RestaurantData>({
212
+ mindstudio.runTask({
213
213
  prompt: '...',
214
214
  input: { name: input.name },
215
215
  tools: ['searchGoogle', 'fetchUrl', 'generateImage'],
216
- structuredOutputExample: { /* ... */ },
216
+ outputSchema: { type: 'object', properties: { /* ... */ }, required: [/* ... */] },
217
217
  model: 'claude-5-sonnet',
218
218
  }).then(async (result) => {
219
- if (result.parsedSuccessfully) {
220
- await Restaurants.update(input.id, { ...result.output, status: 'complete' });
221
- } else {
222
- await Restaurants.update(input.id, { status: 'failed' });
223
- }
219
+ // outputSchema means result.output is validated and typed — a task that
220
+ // can't produce conforming output rejects into the .catch instead.
221
+ await Restaurants.update(input.id, { ...result.output, status: 'complete' });
224
222
  }).catch(async () => {
225
223
  await Restaurants.update(input.id, { status: 'failed' });
226
224
  });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: Task Agents
3
- what: A full autonomous agent loop callable from any method. Give it a prompt, a set of tools, and an example of the output shape; the platform runs the model until it produces that shape — searching, scraping, generating images, retrying approaches that failed, and calling your app's own methods to read and write data as it goes. Tools can be any of the 1000+ SDK actions and your own methods in any combination, which is what makes it part of the app rather than a detached research bot. This is the difference between a feature that saves what the user typed and one that researches, enriches, and creates on their behalf, and it is one of the most powerful things the platform can do. Consider it whenever a feature would be dramatically more compelling if the app could do real work autonomously.
3
+ what: A full autonomous agent loop callable from any method. Give it a prompt, a set of tools, and a JSON Schema for the output shape; the platform runs the model until it produces validated output matching that schema — searching, scraping, generating images, retrying approaches that failed, and calling your app's own methods to read and write data as it goes. Tools can be any of the 1000+ SDK actions and your own methods in any combination, which is what makes it part of the app rather than a detached research bot. This is the difference between a feature that saves what the user typed and one that researches, enriches, and creates on their behalf, and it is one of the most powerful things the platform can do. Consider it whenever a feature would be dramatically more compelling if the app could do real work autonomously.
4
4
  when: Before writing any `mindstudio.runTask()` call — background enrichment, research-and-generate, anything where the model decides its own next step.
5
5
  ---
6
6
 
@@ -8,7 +8,7 @@ when: Before writing any `mindstudio.runTask()` call — background enrichment,
8
8
 
9
9
  A user types the name of a restaurant into your app, or uploads a photo of a storefront. The API call returns early, and in the background, a task agent searches Google, finds the official website, scrapes the address, gets the official social media accounts, and generates a stylized watercolor postcard of the exterior from images it found online. The user gets back a rich, illustrated card with the canonical name, website, address, and a custom image. A few tool calls (some in parallel), fully autonomous.
10
10
 
11
- `runTask()` makes this possible. It runs a multi-step, tool-use agent loop: give it a prompt, a set of tools, and an example of the structured output you want. The platform runs the loop (calling the model, executing tool calls, feeding results back) until the model produces JSON matching your output example. The model decides what to do next based on intermediate results — retrying searches with different terms, working around failed tools, batching independent calls in parallel.
11
+ `runTask()` makes this possible. It runs a multi-step, tool-use agent loop: give it a prompt, a set of tools, and a JSON Schema for the structured output you want (`outputSchema`). The platform runs the loop (calling the model, executing tool calls, feeding results back) until the model produces JSON conforming to your schema — validated every turn, with automatic repair when it doesn't. `result.output` is typed by inference from the schema: no generic argument, no manual validation. The model decides what to do next based on intermediate results — retrying searches with different terms, working around failed tools, batching independent calls in parallel.
12
12
 
13
13
  Tools are **SDK actions** (`searchGoogle`, `generateImage`, …) and **your own app's methods** (`{ appMethod: 'saveVendor' }`), in any combination. That second half is what makes a task agent part of your app rather than a detached research bot: it can read your tables to decide what to do next, and write results back itself instead of handing them to you to persist.
14
14
 
@@ -38,12 +38,7 @@ Run tasks in the background — depending on complexity they can take time to co
38
38
  ```typescript
39
39
  import { mindstudio } from '@mindstudio-ai/agent';
40
40
 
41
- const result = await mindstudio.runTask<{
42
- name: string;
43
- url: string;
44
- address: string;
45
- photoUrl: string;
46
- }>({
41
+ const result = await mindstudio.runTask({
47
42
  prompt: `You are a restaurant research assistant. Given a restaurant name,
48
43
  find its canonical name, website URL, full address, and create a stylized
49
44
  watercolor illustration of the restaurant exterior. Save the result before
@@ -61,30 +56,46 @@ const result = await mindstudio.runTask<{
61
56
  },
62
57
  ],
63
58
 
64
- structuredOutputExample: {
65
- name: 'Tartine Bakery',
66
- url: 'https://tartinebakery.com',
67
- address: '600 Guerrero St, San Francisco, CA 94110',
68
- photoUrl: 'https://cdn.mindstudio.ai/...',
59
+ outputSchema: {
60
+ type: 'object',
61
+ properties: {
62
+ name: { type: 'string' },
63
+ url: { type: ['string', 'null'] }, // nullable = a type array, never `nullable: true`
64
+ address: { type: 'string' },
65
+ photoUrl: { type: 'string' },
66
+ },
67
+ required: ['name', 'url', 'address', 'photoUrl'],
69
68
  },
70
69
 
71
70
  model: 'claude-5-sonnet', // ask askMindStudioSdk — don't copy this one blind
72
71
  maxTurns: 15,
73
72
  });
74
73
 
75
- // Always validate before using output
76
- if (!result.parsedSuccessfully) {
77
- console.error('Task failed to produce structured output:', result.outputRaw);
78
- throw new Error('Task agent failed');
79
- }
80
-
81
- console.log(result.output.name); // 'Tartine Bakery'
82
- console.log(result.output.photoUrl); // URL to the generated illustration
74
+ // result.output is typed from the schema — no generic argument, no validation.
75
+ console.log(result.output.name); // string
76
+ console.log(result.output.url); // string | null
83
77
  ```
84
78
 
85
- ## Always Validate Output
79
+ ## Output Contracts: `outputSchema` vs `structuredOutputExample`
86
80
 
87
- `runTask()` can return successfully with garbage outputfields null, data echoed back, or raw text instead of JSON. The result includes `parsedSuccessfully` to make this explicit. Always check it before using the output:
81
+ **With `outputSchema` (use this):** validation is built in. The schema is plain JSON Schema in the tool-definition dialect `type`, `properties`, `required`, `enum`, `items`, nullability via type arrays like `['string', 'null']`. No `oneOf`/`anyOf`/`$ref`, no `nullable: true` — out-of-dialect schemas are rejected up front with `task_output_schema_unsupported`. Output is validated every turn and repaired automatically; `runTask()` either returns schema-conforming typed output or throws a `MindStudioError` with `code === 'task_output_schema_mismatch'` (raw text and validation errors in `err.details`). It never resolves with garbage, so use `result.output` directly and validate only domain invariants the schema can't express. For dynamic value sets, build the schema at runtime and put the set in an `enum` — the value is checked at runtime even though the type widens to `string`:
82
+
83
+ ```typescript
84
+ import type { JsonObjectSchema } from '@mindstudio-ai/agent';
85
+
86
+ const services = [...new Set(rows.map((r) => r.service))];
87
+ const result = await mindstudio.runTask({
88
+ // ...
89
+ outputSchema: {
90
+ type: 'object',
91
+ properties: { service: { enum: services } },
92
+ required: ['service'],
93
+ } as const satisfies JsonObjectSchema, // needed when the schema lives in a variable
94
+ // ...
95
+ });
96
+ ```
97
+
98
+ **With `structuredOutputExample` (legacy):** the output shape is suggested by example only, `output` is typed by your generic argument, and `runTask()` can return successfully with garbage — fields null, data echoed back, or raw text instead of JSON. Always check `parsedSuccessfully` before using the output:
88
99
 
89
100
  ```typescript
90
101
  const result = await mindstudio.runTask<MyType>({ ... });
@@ -151,7 +162,8 @@ When a task agent produces user-facing text, the prompt must state the voice and
151
162
  | `prompt` | Yes | — | System prompt defining the agent's behavior |
152
163
  | `input` | Yes | — | Structured input (passed as user message) |
153
164
  | `tools` | Yes | — | SDK action names and/or `{ appMethod, description }` entries, each with optional `defaults` |
154
- | `structuredOutputExample` | Yes | — | Object or JSON string showing expected output shape. Use realistic example values, not placeholders like `'string'` |
165
+ | `outputSchema` | One of these two | — | Plain JSON Schema for the output (`type`/`properties`/`required`/`enum`/`items`; nullable via type arrays, never `nullable: true`; no `oneOf`/`$ref`). Validated every turn with automatic repair; `result.output` typed from the schema. Use this |
166
+ | `structuredOutputExample` | One of these two | — | Legacy: object or JSON string showing expected output shape, unvalidated. Use realistic example values, not placeholders like `'string'`, and always check `parsedSuccessfully` |
155
167
  | `model` | Yes | — | Model ID (must support tool use). Ask `askMindStudioSdk` for the right one — MindStudio's ids don't match vendor ids, so a plausible-looking guess is usually wrong |
156
168
  | `maxTurns` | No | 20 | Max loop iterations (capped at 100) |
157
169
  | `onEvent` | No | — | SSE event callback for real-time streaming |
@@ -160,9 +172,9 @@ When a task agent produces user-facing text, the prompt must state the voice and
160
172
 
161
173
  ```typescript
162
174
  interface RunTaskResult<T> {
163
- output: T; // Parsed structured output matching your example
175
+ output: T; // With outputSchema: validated, typed from the schema. With an example: whatever parsed
164
176
  outputRaw: string; // Raw model text before JSON parse
165
- parsedSuccessfully: boolean; // Whether output was valid JSON
177
+ parsedSuccessfully: boolean; // Example mode only always true in schema mode (a failure throws instead)
166
178
  turns: number; // Number of loop iterations used
167
179
  usage: {
168
180
  inputTokens: number;
@@ -201,22 +213,23 @@ Without `onEvent`, the SDK uses async polling (returns silently when complete).
201
213
  ## Error Handling
202
214
 
203
215
  - Model produces non-JSON output: retried automatically if turns remain
216
+ - With `outputSchema`, JSON that doesn't conform: the validation errors go back to the model as a repair turn (up to 3), then the call throws
204
217
  - Tool execution fails: error fed back to model, it can retry or work around it
205
218
  - An app method that throws: its actual error message goes back to the model, so a `MindStudioError` you throw deliberately ("vendor not found", "missing required field") is usable guidance the agent can act on. Worth throwing informative errors in methods you expose as tools
206
219
  - Max turns exceeded: one final forced output attempt with tools disabled
207
- - If output still can't be parsed: `parsedSuccessfully` will be `false`, raw text available in `outputRaw`
220
+ - Exhausted with nonconforming output: schema mode **throws** `task_output_schema_mismatch` (raw text and errors in `err.details`); example mode resolves with `parsedSuccessfully: false` and raw text in `outputRaw`
208
221
 
209
222
  ```typescript
210
223
  try {
211
- const result = await mindstudio.runTask({ ... });
212
- if (!result.parsedSuccessfully) {
213
- // Task completed but output wasn't valid JSON
214
- console.error('Raw output:', result.outputRaw);
215
- console.error('Tool calls:', result.toolCalls);
216
- }
224
+ const result = await mindstudio.runTask({ ...with outputSchema... });
225
+ // Conforming, typed output — use it directly.
226
+ await Table.update(id, result.output);
217
227
  } catch (err) {
218
228
  if (err instanceof MindStudioError) {
219
- // err.code: 'task_execution_error' | 'poll_token_expired' | 'stream_error'
229
+ // err.code: 'task_output_schema_mismatch' (couldn't produce conforming output;
230
+ // err.details has outputRaw + validation errors + toolCalls)
231
+ // | 'task_output_schema_unsupported' (schema uses out-of-dialect keywords)
232
+ // | 'task_execution_error' | 'poll_token_expired' | 'stream_error'
220
233
  }
221
234
  }
222
235
  ```
@@ -64,17 +64,17 @@ When a plan includes multiple screens/API calls, always note this item for the d
64
64
 
65
65
  - **Auth state read once instead of subscribed.** If the plan reads `auth.currentUser` or `auth.getCurrentUser()` in a `useState` initializer, at component top-level, or in a one-time check, the UI won't update after login/logout. The correct pattern is `auth.onAuthStateChanged(cb)` which fires immediately and on every auth transition. Flag if you see auth state read without a subscription.
66
66
 
67
- - **Manual multi-step chains that should be `runTask()`.** If a method chains AI-driven work with branching logic (search, then scrape based on results, then generate based on what was scraped), that's a `runTask()` use case. `runTask()` runs an agent loop that autonomously calls tools and returns structured JSON. The developer writes a prompt and an output example instead of imperative code. Flag when you see methods with complex sequential/branching chains — especially research, enrichment, or content generation pipelines. Similarly, flag opportunities where the developer might not have realized they could get better and richer data via runTask - it's a really powerful lever for working with data (e.g., user provides some fragment and agent task goes off and enriches it) that the developer might not have remembered when planning their work.
67
+ - **Manual multi-step chains that should be `runTask()`.** If a method chains AI-driven work with branching logic (search, then scrape based on results, then generate based on what was scraped), that's a `runTask()` use case. `runTask()` runs an agent loop that autonomously calls tools and returns structured JSON. The developer writes a prompt and a JSON Schema for the output (`outputSchema`) instead of imperative code. Flag when you see methods with complex sequential/branching chains — especially research, enrichment, or content generation pipelines. Similarly, flag opportunities where the developer might not have realized they could get better and richer data via runTask - it's a really powerful lever for working with data (e.g., user provides some fragment and agent task goes off and enriches it) that the developer might not have remembered when planning their work.
68
68
 
69
69
  Tools are SDK actions **and the app's own methods** (`{ appMethod: 'saveVendor', description: '...' }`), which widens this considerably. The pattern most worth looking for: work that needs to *read* app state to decide what to do next (fetch rows, loop, research each, update each) hand-rolled as a loop, when the agent could take a read method and a write method and exercise the judgment itself. App methods run as the invoking user with their roles, so authorization is unchanged.
70
70
 
71
- Two things NOT to flag. A deterministic sequence that happens to touch several methods — if the order is known up front and no judgment is involved, imperative code is correct and cheaper. And the fire-and-forget background pattern, where a method kicks off `runTask()` and writes the result back in `.then()`: that write-back is doing status management around `parsedSuccessfully`, which the agent can't do for itself because it doesn't know when its own output is garbage. That one is the recommended pattern, not a smell.
71
+ Two things NOT to flag. A deterministic sequence that happens to touch several methods — if the order is known up front and no judgment is involved, imperative code is correct and cheaper. And the fire-and-forget background pattern, where a method kicks off `runTask()` and writes the result back in `.then()` with failures landing in `.catch()`: that write-back is doing status management the agent can't do for itself. That one is the recommended pattern, not a smell.
72
72
 
73
73
  - **Task agent tool descriptions missing or recycled.** When a plan exposes app methods to `runTask()`, each entry should carry an inline `description` written for *that* task — when to call it, when not to, what to do with the result. Falling back to the method's own description is allowed but usually too generic, and the description is the main thing determining whether the agent uses the tool correctly. Flag entries with no description, or the same description pasted across different tasks.
74
74
 
75
75
  - **A method exposed as a task tool that itself calls `runTask()`.** Task agents can't nest — the inner call is rejected at runtime. Flag it and suggest flattening the decomposition.
76
76
 
77
- - **MindStudio SDK `runTask()` output used without validation.** `runTask()` can return successfully with garbage output (null fields, echoed input, raw text). The result includes `parsedSuccessfully` if the plan uses `result.output` without checking `result.parsedSuccessfully` first, flag it. This is the #1 footgun with task agents.
77
+ - **MindStudio SDK `runTask()` output used without validation — check which output option the call uses.** With `outputSchema` (the current, preferred form): the SDK validates every turn and throws `MindStudioError` `code === 'task_output_schema_mismatch'` instead of returning garbage, so using `result.output` directly is correct — flag only a missing `.catch()`/`try-catch` where the failure would otherwise vanish (fire-and-forget chains especially). With `structuredOutputExample` (legacy): `runTask()` can return successfully with garbage output (null fields, echoed input, raw text) — flag `result.output` used without checking `result.parsedSuccessfully` first, and suggest migrating to `outputSchema`. That unvalidated form is the #1 footgun with task agents.
78
78
 
79
79
  - **Layout shift with dynamic data or AI generated text** If the plan includes dynamically-sized data (e.g., a wizard form with questions of differing lengths) or AI generated text (where text stream length is unpredictable), make sure to flag concerns about layout stability. Everything must either be a fixed size or smoothly animate between sizes. Text can never be clipped by a container or cause layout to jump around or grow in snappy/janky ways. Make sure to remind the developer that this is important to pay attention to.
80
80
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.277",
3
+ "version": "0.1.278",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",