@mindstudio-ai/remy 0.1.134 → 0.1.136
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/headless.js +72 -31
- package/dist/index.js +83 -32
- package/dist/prompt/.notes.md +1 -1
- package/dist/prompt/compiled/auth.md +4 -1
- package/dist/prompt/compiled/design.md +2 -1
- package/dist/prompt/compiled/methods.md +9 -11
- package/dist/prompt/compiled/msfm.md +21 -2
- package/dist/prompt/compiled/sdk-actions.md +9 -15
- package/dist/prompt/compiled/task-agents.md +10 -8
- package/dist/prompt/static/coding.md +4 -1
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -165,7 +165,12 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
165
165
|
log2.error("Sidecar error", { endpoint, status: res.status });
|
|
166
166
|
throw new Error(`Sidecar error: ${res.status}`);
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
const data = await res.json();
|
|
169
|
+
if (data?.success === false) {
|
|
170
|
+
const code = data.errorCode ? ` [${data.errorCode}]` : "";
|
|
171
|
+
throw new Error(`${data.error || "Unknown error"}${code}`);
|
|
172
|
+
}
|
|
173
|
+
return data;
|
|
169
174
|
} catch (err) {
|
|
170
175
|
if (err.message.startsWith("Sidecar error")) {
|
|
171
176
|
throw err;
|
|
@@ -2602,6 +2607,39 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2602
2607
|
return JSON.stringify({ url, analysis, ...styleMap ? { styleMap } : {} });
|
|
2603
2608
|
}
|
|
2604
2609
|
|
|
2610
|
+
// src/tools/_helpers/browserLock.ts
|
|
2611
|
+
var lockQueue = Promise.resolve();
|
|
2612
|
+
function acquireBrowserLock() {
|
|
2613
|
+
let release;
|
|
2614
|
+
const next = new Promise((res) => {
|
|
2615
|
+
release = res;
|
|
2616
|
+
});
|
|
2617
|
+
const wait = lockQueue;
|
|
2618
|
+
lockQueue = next;
|
|
2619
|
+
return wait.then(() => release);
|
|
2620
|
+
}
|
|
2621
|
+
async function checkBrowserConnected() {
|
|
2622
|
+
try {
|
|
2623
|
+
const status = await sidecarRequest(
|
|
2624
|
+
"/browser-status",
|
|
2625
|
+
{},
|
|
2626
|
+
{ timeout: 5e3 }
|
|
2627
|
+
);
|
|
2628
|
+
if (!status.connected) {
|
|
2629
|
+
return {
|
|
2630
|
+
connected: false,
|
|
2631
|
+
error: "The browser preview is not connected. The user needs to open the preview."
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
return { connected: true };
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
return {
|
|
2637
|
+
connected: false,
|
|
2638
|
+
error: err?.message || "Could not check browser status. The dev environment may not be running."
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2605
2643
|
// src/statusWatcher.ts
|
|
2606
2644
|
function startStatusWatcher(config) {
|
|
2607
2645
|
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
@@ -2743,6 +2781,9 @@ ${summaryBlock.text}
|
|
|
2743
2781
|
return false;
|
|
2744
2782
|
}
|
|
2745
2783
|
}
|
|
2784
|
+
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
2785
|
+
return false;
|
|
2786
|
+
}
|
|
2746
2787
|
if (msg.role === "user" && msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
|
|
2747
2788
|
return false;
|
|
2748
2789
|
}
|
|
@@ -3300,16 +3341,6 @@ ${appSpec}
|
|
|
3300
3341
|
|
|
3301
3342
|
// src/subagents/browserAutomation/index.ts
|
|
3302
3343
|
var log6 = createLogger("browser-automation");
|
|
3303
|
-
var lockQueue = Promise.resolve();
|
|
3304
|
-
function acquireBrowserLock() {
|
|
3305
|
-
let release;
|
|
3306
|
-
const next = new Promise((res) => {
|
|
3307
|
-
release = res;
|
|
3308
|
-
});
|
|
3309
|
-
const wait = lockQueue;
|
|
3310
|
-
lockQueue = next;
|
|
3311
|
-
return wait.then(() => release);
|
|
3312
|
-
}
|
|
3313
3344
|
var browserAutomationTool = {
|
|
3314
3345
|
clearable: true,
|
|
3315
3346
|
definition: {
|
|
@@ -3332,17 +3363,9 @@ var browserAutomationTool = {
|
|
|
3332
3363
|
}
|
|
3333
3364
|
const release = await acquireBrowserLock();
|
|
3334
3365
|
try {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
{},
|
|
3339
|
-
{ timeout: 5e3 }
|
|
3340
|
-
);
|
|
3341
|
-
if (!status.connected) {
|
|
3342
|
-
return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
|
|
3343
|
-
}
|
|
3344
|
-
} catch {
|
|
3345
|
-
return "Error: could not check browser status. The dev environment may not be running.";
|
|
3366
|
+
const browserStatus = await checkBrowserConnected();
|
|
3367
|
+
if (!browserStatus.connected) {
|
|
3368
|
+
return `Error: ${browserStatus.error}`;
|
|
3346
3369
|
}
|
|
3347
3370
|
try {
|
|
3348
3371
|
await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
|
|
@@ -3516,11 +3539,20 @@ var screenshotTool = {
|
|
|
3516
3539
|
...styleMap ? { styleMap } : {}
|
|
3517
3540
|
});
|
|
3518
3541
|
}
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3542
|
+
const release = await acquireBrowserLock();
|
|
3543
|
+
try {
|
|
3544
|
+
const browserStatus = await checkBrowserConnected();
|
|
3545
|
+
if (!browserStatus.connected) {
|
|
3546
|
+
return `Error: ${browserStatus.error}`;
|
|
3547
|
+
}
|
|
3548
|
+
return await captureAndAnalyzeScreenshot({
|
|
3549
|
+
prompt: input.prompt,
|
|
3550
|
+
path: input.path,
|
|
3551
|
+
onLog: context?.onLog
|
|
3552
|
+
});
|
|
3553
|
+
} finally {
|
|
3554
|
+
release();
|
|
3555
|
+
}
|
|
3524
3556
|
} catch (err) {
|
|
3525
3557
|
return `Error taking screenshot: ${err.message}`;
|
|
3526
3558
|
}
|
|
@@ -3846,7 +3878,12 @@ async function execute5(input, onLog, context) {
|
|
|
3846
3878
|
return `Error taking interactive screenshot: ${err.message}`;
|
|
3847
3879
|
}
|
|
3848
3880
|
}
|
|
3881
|
+
const release = await acquireBrowserLock();
|
|
3849
3882
|
try {
|
|
3883
|
+
const browserStatus = await checkBrowserConnected();
|
|
3884
|
+
if (!browserStatus.connected) {
|
|
3885
|
+
return `Error: ${browserStatus.error}`;
|
|
3886
|
+
}
|
|
3850
3887
|
return await captureAndAnalyzeScreenshot({
|
|
3851
3888
|
prompt: input.prompt,
|
|
3852
3889
|
path: input.path,
|
|
@@ -3854,6 +3891,8 @@ async function execute5(input, onLog, context) {
|
|
|
3854
3891
|
});
|
|
3855
3892
|
} catch (err) {
|
|
3856
3893
|
return `Error taking screenshot: ${err.message}`;
|
|
3894
|
+
} finally {
|
|
3895
|
+
release();
|
|
3857
3896
|
}
|
|
3858
3897
|
}
|
|
3859
3898
|
|
|
@@ -5589,10 +5628,12 @@ async function runTurn(params) {
|
|
|
5589
5628
|
saveSession(state);
|
|
5590
5629
|
return;
|
|
5591
5630
|
}
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5631
|
+
if (contentBlocks.length > 0) {
|
|
5632
|
+
state.messages.push({
|
|
5633
|
+
role: "assistant",
|
|
5634
|
+
content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
|
|
5635
|
+
});
|
|
5636
|
+
}
|
|
5596
5637
|
const toolCalls = getToolCalls(contentBlocks);
|
|
5597
5638
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
5598
5639
|
statusWatcher.stop();
|
package/dist/index.js
CHANGED
|
@@ -1986,7 +1986,12 @@ async function sidecarRequest(endpoint, body = {}, options) {
|
|
|
1986
1986
|
log2.error("Sidecar error", { endpoint, status: res.status });
|
|
1987
1987
|
throw new Error(`Sidecar error: ${res.status}`);
|
|
1988
1988
|
}
|
|
1989
|
-
|
|
1989
|
+
const data = await res.json();
|
|
1990
|
+
if (data?.success === false) {
|
|
1991
|
+
const code = data.errorCode ? ` [${data.errorCode}]` : "";
|
|
1992
|
+
throw new Error(`${data.error || "Unknown error"}${code}`);
|
|
1993
|
+
}
|
|
1994
|
+
return data;
|
|
1990
1995
|
} catch (err) {
|
|
1991
1996
|
if (err.message.startsWith("Sidecar error")) {
|
|
1992
1997
|
throw err;
|
|
@@ -2306,6 +2311,46 @@ Respond only with your analysis as Markdown and absolutely no other text. Do not
|
|
|
2306
2311
|
}
|
|
2307
2312
|
});
|
|
2308
2313
|
|
|
2314
|
+
// src/tools/_helpers/browserLock.ts
|
|
2315
|
+
function acquireBrowserLock() {
|
|
2316
|
+
let release;
|
|
2317
|
+
const next = new Promise((res) => {
|
|
2318
|
+
release = res;
|
|
2319
|
+
});
|
|
2320
|
+
const wait = lockQueue;
|
|
2321
|
+
lockQueue = next;
|
|
2322
|
+
return wait.then(() => release);
|
|
2323
|
+
}
|
|
2324
|
+
async function checkBrowserConnected() {
|
|
2325
|
+
try {
|
|
2326
|
+
const status = await sidecarRequest(
|
|
2327
|
+
"/browser-status",
|
|
2328
|
+
{},
|
|
2329
|
+
{ timeout: 5e3 }
|
|
2330
|
+
);
|
|
2331
|
+
if (!status.connected) {
|
|
2332
|
+
return {
|
|
2333
|
+
connected: false,
|
|
2334
|
+
error: "The browser preview is not connected. The user needs to open the preview."
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
return { connected: true };
|
|
2338
|
+
} catch (err) {
|
|
2339
|
+
return {
|
|
2340
|
+
connected: false,
|
|
2341
|
+
error: err?.message || "Could not check browser status. The dev environment may not be running."
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
var lockQueue;
|
|
2346
|
+
var init_browserLock = __esm({
|
|
2347
|
+
"src/tools/_helpers/browserLock.ts"() {
|
|
2348
|
+
"use strict";
|
|
2349
|
+
init_sidecar();
|
|
2350
|
+
lockQueue = Promise.resolve();
|
|
2351
|
+
}
|
|
2352
|
+
});
|
|
2353
|
+
|
|
2309
2354
|
// src/statusWatcher.ts
|
|
2310
2355
|
function startStatusWatcher(config) {
|
|
2311
2356
|
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
@@ -2452,6 +2497,9 @@ ${summaryBlock.text}
|
|
|
2452
2497
|
return false;
|
|
2453
2498
|
}
|
|
2454
2499
|
}
|
|
2500
|
+
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
2501
|
+
return false;
|
|
2502
|
+
}
|
|
2455
2503
|
if (msg.role === "user" && msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
|
|
2456
2504
|
return false;
|
|
2457
2505
|
}
|
|
@@ -3079,16 +3127,7 @@ var init_prompt = __esm({
|
|
|
3079
3127
|
});
|
|
3080
3128
|
|
|
3081
3129
|
// src/subagents/browserAutomation/index.ts
|
|
3082
|
-
|
|
3083
|
-
let release;
|
|
3084
|
-
const next = new Promise((res) => {
|
|
3085
|
-
release = res;
|
|
3086
|
-
});
|
|
3087
|
-
const wait = lockQueue;
|
|
3088
|
-
lockQueue = next;
|
|
3089
|
-
return wait.then(() => release);
|
|
3090
|
-
}
|
|
3091
|
-
var log4, lockQueue, browserAutomationTool;
|
|
3130
|
+
var log4, browserAutomationTool;
|
|
3092
3131
|
var init_browserAutomation = __esm({
|
|
3093
3132
|
"src/subagents/browserAutomation/index.ts"() {
|
|
3094
3133
|
"use strict";
|
|
@@ -3096,11 +3135,11 @@ var init_browserAutomation = __esm({
|
|
|
3096
3135
|
init_tools();
|
|
3097
3136
|
init_prompt();
|
|
3098
3137
|
init_sidecar();
|
|
3138
|
+
init_browserLock();
|
|
3099
3139
|
init_screenshot();
|
|
3100
3140
|
init_runCli();
|
|
3101
3141
|
init_logger();
|
|
3102
3142
|
log4 = createLogger("browser-automation");
|
|
3103
|
-
lockQueue = Promise.resolve();
|
|
3104
3143
|
browserAutomationTool = {
|
|
3105
3144
|
clearable: true,
|
|
3106
3145
|
definition: {
|
|
@@ -3123,17 +3162,9 @@ var init_browserAutomation = __esm({
|
|
|
3123
3162
|
}
|
|
3124
3163
|
const release = await acquireBrowserLock();
|
|
3125
3164
|
try {
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
{},
|
|
3130
|
-
{ timeout: 5e3 }
|
|
3131
|
-
);
|
|
3132
|
-
if (!status.connected) {
|
|
3133
|
-
return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
|
|
3134
|
-
}
|
|
3135
|
-
} catch {
|
|
3136
|
-
return "Error: could not check browser status. The dev environment may not be running.";
|
|
3165
|
+
const browserStatus = await checkBrowserConnected();
|
|
3166
|
+
if (!browserStatus.connected) {
|
|
3167
|
+
return `Error: ${browserStatus.error}`;
|
|
3137
3168
|
}
|
|
3138
3169
|
try {
|
|
3139
3170
|
await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
|
|
@@ -3247,6 +3278,7 @@ var init_screenshot2 = __esm({
|
|
|
3247
3278
|
"src/tools/code/screenshot.ts"() {
|
|
3248
3279
|
"use strict";
|
|
3249
3280
|
init_screenshot();
|
|
3281
|
+
init_browserLock();
|
|
3250
3282
|
init_analyzeImage();
|
|
3251
3283
|
init_browserAutomation();
|
|
3252
3284
|
screenshotTool = {
|
|
@@ -3316,11 +3348,20 @@ var init_screenshot2 = __esm({
|
|
|
3316
3348
|
...styleMap ? { styleMap } : {}
|
|
3317
3349
|
});
|
|
3318
3350
|
}
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3351
|
+
const release = await acquireBrowserLock();
|
|
3352
|
+
try {
|
|
3353
|
+
const browserStatus = await checkBrowserConnected();
|
|
3354
|
+
if (!browserStatus.connected) {
|
|
3355
|
+
return `Error: ${browserStatus.error}`;
|
|
3356
|
+
}
|
|
3357
|
+
return await captureAndAnalyzeScreenshot({
|
|
3358
|
+
prompt: input.prompt,
|
|
3359
|
+
path: input.path,
|
|
3360
|
+
onLog: context?.onLog
|
|
3361
|
+
});
|
|
3362
|
+
} finally {
|
|
3363
|
+
release();
|
|
3364
|
+
}
|
|
3324
3365
|
} catch (err) {
|
|
3325
3366
|
return `Error taking screenshot: ${err.message}`;
|
|
3326
3367
|
}
|
|
@@ -3662,7 +3703,12 @@ async function execute5(input, onLog, context) {
|
|
|
3662
3703
|
return `Error taking interactive screenshot: ${err.message}`;
|
|
3663
3704
|
}
|
|
3664
3705
|
}
|
|
3706
|
+
const release = await acquireBrowserLock();
|
|
3665
3707
|
try {
|
|
3708
|
+
const browserStatus = await checkBrowserConnected();
|
|
3709
|
+
if (!browserStatus.connected) {
|
|
3710
|
+
return `Error: ${browserStatus.error}`;
|
|
3711
|
+
}
|
|
3666
3712
|
return await captureAndAnalyzeScreenshot({
|
|
3667
3713
|
prompt: input.prompt,
|
|
3668
3714
|
path: input.path,
|
|
@@ -3670,6 +3716,8 @@ async function execute5(input, onLog, context) {
|
|
|
3670
3716
|
});
|
|
3671
3717
|
} catch (err) {
|
|
3672
3718
|
return `Error taking screenshot: ${err.message}`;
|
|
3719
|
+
} finally {
|
|
3720
|
+
release();
|
|
3673
3721
|
}
|
|
3674
3722
|
}
|
|
3675
3723
|
var definition5;
|
|
@@ -3677,6 +3725,7 @@ var init_screenshot3 = __esm({
|
|
|
3677
3725
|
"src/subagents/designExpert/tools/screenshot.ts"() {
|
|
3678
3726
|
"use strict";
|
|
3679
3727
|
init_screenshot();
|
|
3728
|
+
init_browserLock();
|
|
3680
3729
|
init_analyzeImage();
|
|
3681
3730
|
init_browserAutomation();
|
|
3682
3731
|
definition5 = {
|
|
@@ -5651,10 +5700,12 @@ async function runTurn(params) {
|
|
|
5651
5700
|
saveSession(state);
|
|
5652
5701
|
return;
|
|
5653
5702
|
}
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5703
|
+
if (contentBlocks.length > 0) {
|
|
5704
|
+
state.messages.push({
|
|
5705
|
+
role: "assistant",
|
|
5706
|
+
content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5658
5709
|
const toolCalls = getToolCalls(contentBlocks);
|
|
5659
5710
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
5660
5711
|
statusWatcher.stop();
|
package/dist/prompt/.notes.md
CHANGED
|
@@ -69,7 +69,7 @@ From research into v0, Lovable, Bolt, and Anthropic's `<frontend_aesthetics>` co
|
|
|
69
69
|
|
|
70
70
|
## SDK Usage
|
|
71
71
|
|
|
72
|
-
The
|
|
72
|
+
The canonical import pattern for MindStudio app methods is `import { mindstudio } from '@mindstudio-ai/agent'` with `mindstudio.generateText(...)`, `mindstudio.runTask(...)`, etc. The `mindstudio` singleton handles auth automatically. `new MindStudioAgent({ apiKey })` is only for external usage outside MindStudio apps.
|
|
73
73
|
|
|
74
74
|
The SDK ships `llms.txt` at the package root with full signatures for all 170+ actions. The compiled fragment references this path (`dist/methods/node_modules/@mindstudio-ai/agent/llms.txt`) so the agent knows where to look up specific action details.
|
|
75
75
|
|
|
@@ -343,13 +343,16 @@ Roles are declared in the manifest, stored as an array column on the user table,
|
|
|
343
343
|
|
|
344
344
|
Apps without `auth` in the manifest use anonymous guest sessions. No login, no user identity, no roles. This is the default and works fine for single-user apps, internal tools, and simple utilities.
|
|
345
345
|
|
|
346
|
-
## Designing Auth in Web Interfaces
|
|
346
|
+
## Important: Designing Auth in Web Interfaces
|
|
347
|
+
|
|
347
348
|
The most imporant user experience consideration with auth is that authentication moments must feel natural and intuitive - they should not feel jarring or surprising. Take care to integrate them into the entire experience when building.
|
|
348
349
|
|
|
349
350
|
For the overwhelming majority of apps, a user should never land on auth at the root of an app when opening it for the first time (except in cases where the app is, e.g., an internal tool or some other protected experience - and even then it should feel more like a welcome/splash screen than an error state). Users should be able to explore public resources, or at least encounter some kind of landing/introduction moment, before they get hit with a signup/login screen. Make auth feel like a natural moment in the user's journey.
|
|
350
351
|
|
|
351
352
|
Login and signup screens set the tone for the user's entire experience with the app and are important to get right - they should feel like exciting entry points into the next level of the user journy. A janky login form with misaligned inputs and no feedback dminishes excitement and undermines trust before the user even gets in.
|
|
352
353
|
|
|
354
|
+
Login and signup are separate moments - even if the underlying code is the same for both. A new user signing up should feel like they are creating a new account. A user logging in should feel like they are being welcomed back. Auth is always a pain for users, even when it's as frictionless as this, so take care to use Sign Up screens as moments to communicate value and help the user get excited about what they are joining.
|
|
355
|
+
|
|
353
356
|
Consult the `visualDesignExpert` to help you work through authentication at a high level, including when and where to show auth, and the design of specific screens.
|
|
354
357
|
|
|
355
358
|
### Rules for Building Auth Screens
|
|
@@ -25,7 +25,7 @@ Interfaces run fullscreen in the user's browser or a wrapped webview mobile app.
|
|
|
25
25
|
- **No long scrolling pages.** Use structured layouts: cards, split panes, steppers, tabs, grouped sections that fit the viewport. The interface should feel like an award winning iOS or macOS app, not a document.
|
|
26
26
|
- **On mobile**, scrolling may be necessary, but use sticky headers, fixed CTAs, and anchored navigation to keep key actions within reach. Always use "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" to make sure apps feel like apps.
|
|
27
27
|
- Think of every screen as something the user opens, uses, and closes — not something they read.
|
|
28
|
-
- Pay attention to details that will make things feel app-like - set user-select: none on specific app-like layout elements, use motion, use iOS/macOS design language and patterns.
|
|
28
|
+
- Pay attention to details that will make things feel app-like - set user-select: none on specific app-like layout elements like navigation bars, use motion, use iOS/macOS design language and patterns.
|
|
29
29
|
|
|
30
30
|
## Layout Stability
|
|
31
31
|
|
|
@@ -50,6 +50,7 @@ Every interface must work on both desktop and mobile. Think about how the app wi
|
|
|
50
50
|
- Even for mobile-first apps, make sure to set desktop or larger device breakpoints - nothing looks jankier than opening a mobile-designed site in a desktop browser and seeing a full width bottom tab bar with nav icons stretching 1000px wide. Don't make sloppy, amateur mistakes or omissions like this - the user will notice them and be disappointed.
|
|
51
51
|
|
|
52
52
|
## Images
|
|
53
|
+
|
|
53
54
|
The `designExpert` can create and source amazing, high quality images, graphics, illustrations, and logos to use in the interface - both with and without transparency. This is a huge level for upgrading the premium look, feel, and quality of the app. Use image logos directly instead of plain text wordmarks; use images for empty states, onboarding screens, full-screen loading, and more.
|
|
54
55
|
|
|
55
56
|
## Forms
|
|
@@ -65,38 +65,36 @@ export async function getDashboard(input: {
|
|
|
65
65
|
|
|
66
66
|
## Platform Capabilities
|
|
67
67
|
|
|
68
|
-
The `@mindstudio-ai/agent` SDK provides access to 200+ AI models and 1,000+ actions (email, SMS, web scraping, file uploads, third-party integrations, and more). Inside a method,
|
|
68
|
+
The `@mindstudio-ai/agent` SDK provides access to 200+ AI models and 1,000+ actions (email, SMS, web scraping, file uploads, third-party integrations, and more). Inside a method, use the `mindstudio` singleton — credentials come from the execution environment automatically:
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
|
-
import {
|
|
72
|
-
|
|
73
|
-
const agent = new MindStudioAgent();
|
|
71
|
+
import { mindstudio } from '@mindstudio-ai/agent';
|
|
74
72
|
|
|
75
73
|
// AI text generation
|
|
76
|
-
const { content } = await
|
|
74
|
+
const { content } = await mindstudio.generateText({
|
|
77
75
|
message: 'Summarize this invoice...',
|
|
78
76
|
});
|
|
79
77
|
|
|
80
78
|
// AI image generation
|
|
81
|
-
const { imageUrl } = await
|
|
79
|
+
const { imageUrl } = await mindstudio.generateImage({
|
|
82
80
|
prompt: 'A professional headshot placeholder',
|
|
83
81
|
});
|
|
84
82
|
|
|
85
83
|
// Send email
|
|
86
|
-
await
|
|
84
|
+
await mindstudio.sendEmail({
|
|
87
85
|
to: 'user@example.com',
|
|
88
86
|
subject: 'Your invoice',
|
|
89
87
|
body: content,
|
|
90
88
|
});
|
|
91
89
|
|
|
92
90
|
// Upload files
|
|
93
|
-
const { url } = await
|
|
91
|
+
const { url } = await mindstudio.uploadFile({
|
|
94
92
|
data: buffer,
|
|
95
93
|
fileName: 'report.pdf',
|
|
96
94
|
});
|
|
97
95
|
|
|
98
96
|
// Web scraping
|
|
99
|
-
const { markdown } = await
|
|
97
|
+
const { markdown } = await mindstudio.scrapeUrl({
|
|
100
98
|
url: 'https://example.com',
|
|
101
99
|
});
|
|
102
100
|
|
|
@@ -126,7 +124,7 @@ For errors from external services or internal failures (API calls, AI generation
|
|
|
126
124
|
|
|
127
125
|
```typescript
|
|
128
126
|
try {
|
|
129
|
-
const result = await
|
|
127
|
+
const result = await mindstudio.generateVideo({ ... });
|
|
130
128
|
return { videoUrl: result.url };
|
|
131
129
|
} catch (err) {
|
|
132
130
|
console.error('Video generation failed:', err);
|
|
@@ -210,7 +208,7 @@ export async function enrichRestaurant(input: { id: string; name: string }) {
|
|
|
210
208
|
await Restaurants.update(input.id, { status: 'enriching' });
|
|
211
209
|
|
|
212
210
|
// Fire — don't await
|
|
213
|
-
|
|
211
|
+
mindstudio.runTask<RestaurantData>({
|
|
214
212
|
prompt: '...',
|
|
215
213
|
input: { name: input.name },
|
|
216
214
|
tools: ['searchGoogle', 'fetchUrl', 'generateImage'],
|
|
@@ -192,13 +192,15 @@ styles:
|
|
|
192
192
|
```
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
Roadmap item
|
|
195
|
+
Roadmap item examples (one file per feature in `src/roadmap/`):
|
|
196
|
+
|
|
197
|
+
Built item (status updated to `done` after build, with History appended):
|
|
196
198
|
|
|
197
199
|
```markdown
|
|
198
200
|
---
|
|
199
201
|
name: Share & Export
|
|
200
202
|
type: roadmap
|
|
201
|
-
status:
|
|
203
|
+
status: done
|
|
202
204
|
description: Share haikus as image cards to social media or download as prints.
|
|
203
205
|
requires: []
|
|
204
206
|
effort: medium
|
|
@@ -220,3 +222,20 @@ clipboard fallback for unsupported browsers.
|
|
|
220
222
|
- **2026-03-22** — Built card generation using generateImage.
|
|
221
223
|
Added share button to haiku detail view.
|
|
222
224
|
```
|
|
225
|
+
|
|
226
|
+
Unbuilt item:
|
|
227
|
+
|
|
228
|
+
```markdown
|
|
229
|
+
---
|
|
230
|
+
name: Daily Prompt Engine
|
|
231
|
+
type: roadmap
|
|
232
|
+
status: not-started
|
|
233
|
+
description: A new writing prompt every day, tuned to the user's style and interests.
|
|
234
|
+
requires: []
|
|
235
|
+
effort: small
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
Generate a personalized daily writing prompt based on the user's past haikus,
|
|
239
|
+
preferred themes, and seasonal context. Surface it as a gentle nudge on the
|
|
240
|
+
home screen, not a notification.
|
|
241
|
+
```
|
|
@@ -6,24 +6,18 @@ There is a huge amount of capability here: hundreds of text generation models (O
|
|
|
6
6
|
|
|
7
7
|
## Usage in Methods
|
|
8
8
|
|
|
9
|
-
Inside a MindStudio app method,
|
|
9
|
+
Inside a MindStudio app method, use the `mindstudio` singleton — credentials come from the execution environment automatically:
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
import {
|
|
12
|
+
import { mindstudio } from '@mindstudio-ai/agent';
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Every action is a method on the agent instance:
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
const { content } = await agent.generateText({ message: 'Summarize this...' });
|
|
14
|
+
const { content } = await mindstudio.generateText({ message: 'Summarize this...' });
|
|
21
15
|
```
|
|
22
16
|
|
|
23
17
|
Results are returned flat — output fields at the top level alongside metadata:
|
|
24
18
|
|
|
25
19
|
```typescript
|
|
26
|
-
const result = await
|
|
20
|
+
const result = await mindstudio.generateText({ message: 'Hello' });
|
|
27
21
|
result.content; // step-specific output
|
|
28
22
|
result.$billingCost; // cost in credits (if applicable)
|
|
29
23
|
```
|
|
@@ -104,13 +98,13 @@ For other services, use `runFromConnectorRegistry`:
|
|
|
104
98
|
|
|
105
99
|
```typescript
|
|
106
100
|
// Discover available connectors
|
|
107
|
-
const { connectors } = await
|
|
101
|
+
const { connectors } = await mindstudio.listConnectors();
|
|
108
102
|
|
|
109
103
|
// Get action details
|
|
110
|
-
const action = await
|
|
104
|
+
const action = await mindstudio.getConnectorAction('hubspot', 'create-contact');
|
|
111
105
|
|
|
112
106
|
// Execute
|
|
113
|
-
const result = await
|
|
107
|
+
const result = await mindstudio.runFromConnectorRegistry({
|
|
114
108
|
serviceId: 'hubspot',
|
|
115
109
|
actionId: 'create-contact',
|
|
116
110
|
input: { email: 'user@example.com', firstName: 'Alice' },
|
|
@@ -122,7 +116,7 @@ const result = await agent.runFromConnectorRegistry({
|
|
|
122
116
|
Override the default model for any AI action. Each model has its own config options (dimensions, seed, inference steps, etc.) so always use `askMindStudioSdk` to look up the correct config before specifying a model override:
|
|
123
117
|
|
|
124
118
|
```typescript
|
|
125
|
-
const { content } = await
|
|
119
|
+
const { content } = await mindstudio.generateText({
|
|
126
120
|
message: 'Hello',
|
|
127
121
|
modelOverride: {
|
|
128
122
|
model: 'claude-sonnet-4-6',
|
|
@@ -139,7 +133,7 @@ Make sure to prioritize new, popular models. MindStudio has a ton of models avai
|
|
|
139
133
|
Run up to 50 actions in parallel:
|
|
140
134
|
|
|
141
135
|
```typescript
|
|
142
|
-
const result = await
|
|
136
|
+
const result = await mindstudio.executeStepBatch([
|
|
143
137
|
{ stepType: 'generateImage', step: { prompt: 'a sunset' } },
|
|
144
138
|
{ stepType: 'textToSpeech', step: { text: 'hello world' } },
|
|
145
139
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Task Agents (`
|
|
1
|
+
# Task Agents (`mindstudio.runTask`)
|
|
2
2
|
|
|
3
3
|
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.
|
|
4
4
|
|
|
@@ -26,11 +26,9 @@ Run tasks in the background — depending on complexity they can take time to co
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
28
|
```typescript
|
|
29
|
-
import {
|
|
29
|
+
import { mindstudio } from '@mindstudio-ai/agent';
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const result = await agent.runTask<{
|
|
31
|
+
const result = await mindstudio.runTask<{
|
|
34
32
|
name: string;
|
|
35
33
|
url: string;
|
|
36
34
|
address: string;
|
|
@@ -74,7 +72,7 @@ console.log(result.output.photoUrl); // URL to the generated illustration
|
|
|
74
72
|
`runTask()` can return successfully with garbage output — fields 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:
|
|
75
73
|
|
|
76
74
|
```typescript
|
|
77
|
-
const result = await
|
|
75
|
+
const result = await mindstudio.runTask<MyType>({ ... });
|
|
78
76
|
|
|
79
77
|
if (!result.parsedSuccessfully) {
|
|
80
78
|
console.error('Task output was not valid JSON:', result.outputRaw);
|
|
@@ -106,6 +104,10 @@ tools: [
|
|
|
106
104
|
|
|
107
105
|
When the model calls a tool, the platform deep-merges the model's arguments with the developer's defaults. The model decides what to do (prompt, query, parameters), the developer controls which model/config to use. If the model needs to search and generate an image and those are independent, it will call both tools in the same turn (parallel execution server-side).
|
|
108
106
|
|
|
107
|
+
## Voice & Tone in Prompts
|
|
108
|
+
|
|
109
|
+
When a task agent produces user-facing text, the prompt must include a note voice and tone constraints. Make sure to specify no emojis, em dashes, and other "ai-isms" in the prompt, as well as the desired tone and voice of the output.
|
|
110
|
+
|
|
109
111
|
## Options
|
|
110
112
|
|
|
111
113
|
| Field | Required | Default | Description |
|
|
@@ -150,7 +152,7 @@ When something goes wrong, `toolCalls` is the first thing to check. If it's empt
|
|
|
150
152
|
Pass an `onEvent` callback to get real-time events:
|
|
151
153
|
|
|
152
154
|
```typescript
|
|
153
|
-
const result = await
|
|
155
|
+
const result = await mindstudio.runTask({
|
|
154
156
|
// ... same options ...
|
|
155
157
|
onEvent: (event) => {
|
|
156
158
|
if (event.type === 'text') console.log('Agent:', event.text);
|
|
@@ -173,7 +175,7 @@ Without `onEvent`, the SDK uses async polling (returns silently when complete).
|
|
|
173
175
|
|
|
174
176
|
```typescript
|
|
175
177
|
try {
|
|
176
|
-
const result = await
|
|
178
|
+
const result = await mindstudio.runTask({ ... });
|
|
177
179
|
if (!result.parsedSuccessfully) {
|
|
178
180
|
// Task completed but output wasn't valid JSON
|
|
179
181
|
console.error('Raw output:', result.outputRaw);
|
|
@@ -45,6 +45,9 @@ For multi-step tasks with branching logic (research, enrichment, content pipelin
|
|
|
45
45
|
- Use container queries for components that need to adapt to their container rather than the viewport.
|
|
46
46
|
- For canvas-based UIs (games, visualizations, interactive graphics): size the canvas to fill its container, account for `devicePixelRatio` for Retina sharpness, and scale all objects relative to the viewport — not in fixed pixel sizes - otherwise they are going to be tiny and unusable.
|
|
47
47
|
|
|
48
|
+
### Copy & Prose
|
|
49
|
+
- Landing pages, hero sections, onboarding flows, and other long-form prose need a human voice. Write plainly and specifically. Avoid promotional filler, hollow intensifiers, and chatbot enthusiasm. Avoid em dashes, emojis, and other "ai-isms" in your writing.
|
|
50
|
+
|
|
48
51
|
### Error Visibility
|
|
49
52
|
- Runtime errors must render visibly on screen, not produce a blank white page. User and agent must be able to visibly debug and spot them.
|
|
50
53
|
|
|
@@ -63,7 +66,7 @@ When integrating with external services that have programmable setup APIs (webho
|
|
|
63
66
|
Before installing a package you haven't used in this project, do a quick web search to confirm it's still the best option. The JavaScript ecosystem moves fast — the package you remember from training may have been superseded by something smaller, faster, or better maintained. A 10-second search beats debugging a deprecated library.
|
|
64
67
|
|
|
65
68
|
### MindStudio SDK CLI
|
|
66
|
-
You have access to the `mindstudio` CLI, which exposes every SDK action as a command-line tool. Use it via bash for one-off tasks: generating images, scraping URLs, sending emails, running AI completions, or anything else the SDK can do. Every JavaScript SDK method has a corresponding CLI command. Run `askMindStudioSdk` to discover commands for CLI usage.
|
|
69
|
+
You have access to the `mindstudio` CLI, which exposes every SDK action as a command-line tool. Use it via bash for one-off tasks: generating images, video, or audio, scraping URLs, sending emails, running AI completions, or anything else the SDK can do. Every JavaScript SDK method has a corresponding CLI command. Run `askMindStudioSdk` to discover commands for CLI usage.
|
|
67
70
|
|
|
68
71
|
### Production App Management
|
|
69
72
|
You have access to `mindstudio-prod`, a CLI for managing the user's production MindStudio 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.
|