@mindstudio-ai/remy 0.1.57 → 0.1.59
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 +54 -7
- package/dist/index.js +54 -7
- package/dist/prompt/compiled/interfaces.md +9 -5
- package/dist/prompt/static/team.md +3 -1
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -647,7 +647,7 @@ var CONVERSATION_SUMMARY_PROMPT = readAsset("compaction", "conversation.md");
|
|
|
647
647
|
var SUBAGENT_SUMMARY_PROMPT = readAsset("compaction", "subagent.md");
|
|
648
648
|
var SUMMARIZABLE_SUBAGENTS = ["visualDesignExpert", "productVision"];
|
|
649
649
|
async function compactConversation(state, apiConfig) {
|
|
650
|
-
const insertionIndex = state.messages
|
|
650
|
+
const insertionIndex = findSafeInsertionPoint(state.messages);
|
|
651
651
|
const summaries = [];
|
|
652
652
|
const tasks = [];
|
|
653
653
|
const conversationMessages = getConversationMessagesForSummary(
|
|
@@ -711,6 +711,29 @@ async function compactConversation(state, apiConfig) {
|
|
|
711
711
|
messagesAfter: state.messages.length - insertionIndex - checkpointMessages.length
|
|
712
712
|
});
|
|
713
713
|
}
|
|
714
|
+
function findSafeInsertionPoint(messages) {
|
|
715
|
+
let idx = messages.length;
|
|
716
|
+
while (idx > 0) {
|
|
717
|
+
const msg = messages[idx - 1];
|
|
718
|
+
if (msg.role === "user" && msg.toolCallId) {
|
|
719
|
+
idx--;
|
|
720
|
+
} else {
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (idx < messages.length && idx > 0) {
|
|
725
|
+
const msg = messages[idx - 1];
|
|
726
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
727
|
+
const hasToolUse = msg.content.some(
|
|
728
|
+
(b) => b.type === "tool"
|
|
729
|
+
);
|
|
730
|
+
if (hasToolUse) {
|
|
731
|
+
idx--;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return idx;
|
|
736
|
+
}
|
|
714
737
|
function getConversationMessagesForSummary(messages, endIndex) {
|
|
715
738
|
let startIdx = 0;
|
|
716
739
|
for (let i = endIndex - 1; i >= 0; i--) {
|
|
@@ -2327,9 +2350,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2327
2350
|
let prompt;
|
|
2328
2351
|
let existingUrl;
|
|
2329
2352
|
let onLog;
|
|
2353
|
+
let path9;
|
|
2330
2354
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
2331
2355
|
prompt = promptOrOptions.prompt;
|
|
2332
2356
|
existingUrl = promptOrOptions.imageUrl;
|
|
2357
|
+
path9 = promptOrOptions.path;
|
|
2333
2358
|
onLog = promptOrOptions.onLog;
|
|
2334
2359
|
} else {
|
|
2335
2360
|
prompt = promptOrOptions;
|
|
@@ -2338,9 +2363,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2338
2363
|
if (existingUrl) {
|
|
2339
2364
|
url = existingUrl;
|
|
2340
2365
|
} else {
|
|
2341
|
-
const ssResult = await sidecarRequest(
|
|
2342
|
-
|
|
2343
|
-
|
|
2366
|
+
const ssResult = await sidecarRequest(
|
|
2367
|
+
"/screenshot-full-page",
|
|
2368
|
+
path9 ? { path: path9 } : void 0,
|
|
2369
|
+
{ timeout: 12e4 }
|
|
2370
|
+
);
|
|
2344
2371
|
url = ssResult?.url || ssResult?.screenshotUrl;
|
|
2345
2372
|
if (!url) {
|
|
2346
2373
|
throw new Error(
|
|
@@ -2375,6 +2402,10 @@ var screenshotTool = {
|
|
|
2375
2402
|
imageUrl: {
|
|
2376
2403
|
type: "string",
|
|
2377
2404
|
description: "URL of an existing screenshot to analyze instead of capturing a new one. Use this for additional questions about a previous screenshot."
|
|
2405
|
+
},
|
|
2406
|
+
path: {
|
|
2407
|
+
type: "string",
|
|
2408
|
+
description: 'Navigate to this path before capturing (e.g. "/settings", "/dashboard"). If omitted, screenshots the current page.'
|
|
2378
2409
|
}
|
|
2379
2410
|
}
|
|
2380
2411
|
}
|
|
@@ -2390,6 +2421,7 @@ var screenshotTool = {
|
|
|
2390
2421
|
}
|
|
2391
2422
|
return await captureAndAnalyzeScreenshot({
|
|
2392
2423
|
prompt: input.prompt,
|
|
2424
|
+
path: input.path,
|
|
2393
2425
|
onLog: context?.onLog
|
|
2394
2426
|
});
|
|
2395
2427
|
} catch (err) {
|
|
@@ -2402,6 +2434,7 @@ var screenshotTool = {
|
|
|
2402
2434
|
function startStatusWatcher(config) {
|
|
2403
2435
|
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
2404
2436
|
let lastLabel = "";
|
|
2437
|
+
let lastContext = "";
|
|
2405
2438
|
let inflight = false;
|
|
2406
2439
|
let stopped = false;
|
|
2407
2440
|
const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
|
|
@@ -2412,9 +2445,10 @@ function startStatusWatcher(config) {
|
|
|
2412
2445
|
inflight = true;
|
|
2413
2446
|
try {
|
|
2414
2447
|
const context = getContext();
|
|
2415
|
-
if (!context) {
|
|
2448
|
+
if (!context || context === lastContext) {
|
|
2416
2449
|
return;
|
|
2417
2450
|
}
|
|
2451
|
+
lastContext = context;
|
|
2418
2452
|
const res = await fetch(url, {
|
|
2419
2453
|
method: "POST",
|
|
2420
2454
|
headers: {
|
|
@@ -2960,7 +2994,12 @@ var BROWSER_TOOLS = [
|
|
|
2960
2994
|
description: "Capture a full-height screenshot of the current page. Returns a CDN URL with full text analysis and description.",
|
|
2961
2995
|
inputSchema: {
|
|
2962
2996
|
type: "object",
|
|
2963
|
-
properties: {
|
|
2997
|
+
properties: {
|
|
2998
|
+
path: {
|
|
2999
|
+
type: "string",
|
|
3000
|
+
description: 'Navigate to this path before capturing (e.g. "/settings"). If omitted, screenshots the current page.'
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
2964
3003
|
}
|
|
2965
3004
|
},
|
|
2966
3005
|
{
|
|
@@ -3033,7 +3072,10 @@ var browserAutomationTool = {
|
|
|
3033
3072
|
executeTool: async (name, _input, _toolCallId, onLog) => {
|
|
3034
3073
|
if (name === "screenshotFullPage") {
|
|
3035
3074
|
try {
|
|
3036
|
-
return await captureAndAnalyzeScreenshot({
|
|
3075
|
+
return await captureAndAnalyzeScreenshot({
|
|
3076
|
+
path: _input.path,
|
|
3077
|
+
onLog
|
|
3078
|
+
});
|
|
3037
3079
|
} catch (err) {
|
|
3038
3080
|
return `Error taking screenshot: ${err.message}`;
|
|
3039
3081
|
}
|
|
@@ -3285,6 +3327,10 @@ var definition5 = {
|
|
|
3285
3327
|
prompt: {
|
|
3286
3328
|
type: "string",
|
|
3287
3329
|
description: "Optional specific question about the screenshot."
|
|
3330
|
+
},
|
|
3331
|
+
path: {
|
|
3332
|
+
type: "string",
|
|
3333
|
+
description: 'Navigate to this path before capturing (e.g. "/settings"). If omitted, screenshots the current page.'
|
|
3288
3334
|
}
|
|
3289
3335
|
}
|
|
3290
3336
|
}
|
|
@@ -3293,6 +3339,7 @@ async function execute5(input, onLog) {
|
|
|
3293
3339
|
try {
|
|
3294
3340
|
return await captureAndAnalyzeScreenshot({
|
|
3295
3341
|
prompt: input.prompt,
|
|
3342
|
+
path: input.path,
|
|
3296
3343
|
onLog
|
|
3297
3344
|
});
|
|
3298
3345
|
} catch (err) {
|
package/dist/index.js
CHANGED
|
@@ -2056,9 +2056,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2056
2056
|
let prompt;
|
|
2057
2057
|
let existingUrl;
|
|
2058
2058
|
let onLog;
|
|
2059
|
+
let path10;
|
|
2059
2060
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
2060
2061
|
prompt = promptOrOptions.prompt;
|
|
2061
2062
|
existingUrl = promptOrOptions.imageUrl;
|
|
2063
|
+
path10 = promptOrOptions.path;
|
|
2062
2064
|
onLog = promptOrOptions.onLog;
|
|
2063
2065
|
} else {
|
|
2064
2066
|
prompt = promptOrOptions;
|
|
@@ -2067,9 +2069,11 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
2067
2069
|
if (existingUrl) {
|
|
2068
2070
|
url = existingUrl;
|
|
2069
2071
|
} else {
|
|
2070
|
-
const ssResult = await sidecarRequest(
|
|
2071
|
-
|
|
2072
|
-
|
|
2072
|
+
const ssResult = await sidecarRequest(
|
|
2073
|
+
"/screenshot-full-page",
|
|
2074
|
+
path10 ? { path: path10 } : void 0,
|
|
2075
|
+
{ timeout: 12e4 }
|
|
2076
|
+
);
|
|
2073
2077
|
url = ssResult?.url || ssResult?.screenshotUrl;
|
|
2074
2078
|
if (!url) {
|
|
2075
2079
|
throw new Error(
|
|
@@ -2118,6 +2122,10 @@ var init_screenshot2 = __esm({
|
|
|
2118
2122
|
imageUrl: {
|
|
2119
2123
|
type: "string",
|
|
2120
2124
|
description: "URL of an existing screenshot to analyze instead of capturing a new one. Use this for additional questions about a previous screenshot."
|
|
2125
|
+
},
|
|
2126
|
+
path: {
|
|
2127
|
+
type: "string",
|
|
2128
|
+
description: 'Navigate to this path before capturing (e.g. "/settings", "/dashboard"). If omitted, screenshots the current page.'
|
|
2121
2129
|
}
|
|
2122
2130
|
}
|
|
2123
2131
|
}
|
|
@@ -2133,6 +2141,7 @@ var init_screenshot2 = __esm({
|
|
|
2133
2141
|
}
|
|
2134
2142
|
return await captureAndAnalyzeScreenshot({
|
|
2135
2143
|
prompt: input.prompt,
|
|
2144
|
+
path: input.path,
|
|
2136
2145
|
onLog: context?.onLog
|
|
2137
2146
|
});
|
|
2138
2147
|
} catch (err) {
|
|
@@ -2147,6 +2156,7 @@ var init_screenshot2 = __esm({
|
|
|
2147
2156
|
function startStatusWatcher(config) {
|
|
2148
2157
|
const { apiConfig, getContext, onStatus, interval = 3e3, signal } = config;
|
|
2149
2158
|
let lastLabel = "";
|
|
2159
|
+
let lastContext = "";
|
|
2150
2160
|
let inflight = false;
|
|
2151
2161
|
let stopped = false;
|
|
2152
2162
|
const url = `${apiConfig.baseUrl}/_internal/v2/agent/remy/generate-status`;
|
|
@@ -2157,9 +2167,10 @@ function startStatusWatcher(config) {
|
|
|
2157
2167
|
inflight = true;
|
|
2158
2168
|
try {
|
|
2159
2169
|
const context = getContext();
|
|
2160
|
-
if (!context) {
|
|
2170
|
+
if (!context || context === lastContext) {
|
|
2161
2171
|
return;
|
|
2162
2172
|
}
|
|
2173
|
+
lastContext = context;
|
|
2163
2174
|
const res = await fetch(url, {
|
|
2164
2175
|
method: "POST",
|
|
2165
2176
|
headers: {
|
|
@@ -2729,7 +2740,12 @@ var init_tools = __esm({
|
|
|
2729
2740
|
description: "Capture a full-height screenshot of the current page. Returns a CDN URL with full text analysis and description.",
|
|
2730
2741
|
inputSchema: {
|
|
2731
2742
|
type: "object",
|
|
2732
|
-
properties: {
|
|
2743
|
+
properties: {
|
|
2744
|
+
path: {
|
|
2745
|
+
type: "string",
|
|
2746
|
+
description: 'Navigate to this path before capturing (e.g. "/settings"). If omitted, screenshots the current page.'
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2733
2749
|
}
|
|
2734
2750
|
},
|
|
2735
2751
|
{
|
|
@@ -2865,7 +2881,10 @@ var init_browserAutomation = __esm({
|
|
|
2865
2881
|
executeTool: async (name, _input, _toolCallId, onLog) => {
|
|
2866
2882
|
if (name === "screenshotFullPage") {
|
|
2867
2883
|
try {
|
|
2868
|
-
return await captureAndAnalyzeScreenshot({
|
|
2884
|
+
return await captureAndAnalyzeScreenshot({
|
|
2885
|
+
path: _input.path,
|
|
2886
|
+
onLog
|
|
2887
|
+
});
|
|
2869
2888
|
} catch (err) {
|
|
2870
2889
|
return `Error taking screenshot: ${err.message}`;
|
|
2871
2890
|
}
|
|
@@ -3143,6 +3162,7 @@ async function execute5(input, onLog) {
|
|
|
3143
3162
|
try {
|
|
3144
3163
|
return await captureAndAnalyzeScreenshot({
|
|
3145
3164
|
prompt: input.prompt,
|
|
3165
|
+
path: input.path,
|
|
3146
3166
|
onLog
|
|
3147
3167
|
});
|
|
3148
3168
|
} catch (err) {
|
|
@@ -3163,6 +3183,10 @@ var init_screenshot3 = __esm({
|
|
|
3163
3183
|
prompt: {
|
|
3164
3184
|
type: "string",
|
|
3165
3185
|
description: "Optional specific question about the screenshot."
|
|
3186
|
+
},
|
|
3187
|
+
path: {
|
|
3188
|
+
type: "string",
|
|
3189
|
+
description: 'Navigate to this path before capturing (e.g. "/settings"). If omitted, screenshots the current page.'
|
|
3166
3190
|
}
|
|
3167
3191
|
}
|
|
3168
3192
|
}
|
|
@@ -5609,7 +5633,7 @@ var init_config = __esm({
|
|
|
5609
5633
|
|
|
5610
5634
|
// src/compaction/index.ts
|
|
5611
5635
|
async function compactConversation(state, apiConfig) {
|
|
5612
|
-
const insertionIndex = state.messages
|
|
5636
|
+
const insertionIndex = findSafeInsertionPoint(state.messages);
|
|
5613
5637
|
const summaries = [];
|
|
5614
5638
|
const tasks = [];
|
|
5615
5639
|
const conversationMessages = getConversationMessagesForSummary(
|
|
@@ -5673,6 +5697,29 @@ async function compactConversation(state, apiConfig) {
|
|
|
5673
5697
|
messagesAfter: state.messages.length - insertionIndex - checkpointMessages.length
|
|
5674
5698
|
});
|
|
5675
5699
|
}
|
|
5700
|
+
function findSafeInsertionPoint(messages) {
|
|
5701
|
+
let idx = messages.length;
|
|
5702
|
+
while (idx > 0) {
|
|
5703
|
+
const msg = messages[idx - 1];
|
|
5704
|
+
if (msg.role === "user" && msg.toolCallId) {
|
|
5705
|
+
idx--;
|
|
5706
|
+
} else {
|
|
5707
|
+
break;
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
if (idx < messages.length && idx > 0) {
|
|
5711
|
+
const msg = messages[idx - 1];
|
|
5712
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
5713
|
+
const hasToolUse = msg.content.some(
|
|
5714
|
+
(b) => b.type === "tool"
|
|
5715
|
+
);
|
|
5716
|
+
if (hasToolUse) {
|
|
5717
|
+
idx--;
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
return idx;
|
|
5722
|
+
}
|
|
5676
5723
|
function getConversationMessagesForSummary(messages, endIndex) {
|
|
5677
5724
|
let startIdx = 0;
|
|
5678
5725
|
for (let i = endIndex - 1; i >= 0; i--) {
|
|
@@ -26,12 +26,16 @@ dist/interfaces/web/
|
|
|
26
26
|
|
|
27
27
|
```json
|
|
28
28
|
{
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
"web": {
|
|
30
|
+
"devPort": 5173,
|
|
31
|
+
"devCommand": "npm run dev",
|
|
32
|
+
"defaultPreviewMode": "desktop"
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
35
|
```
|
|
34
36
|
|
|
37
|
+
All fields are nested under the `"web"` key.
|
|
38
|
+
|
|
35
39
|
| Field | Type | Default | Description |
|
|
36
40
|
|-------|------|---------|-------------|
|
|
37
41
|
| `devPort` | `number` | `5173` | Port for the dev server |
|
|
@@ -99,7 +103,7 @@ Omit the `methods` field (or the config entirely) to expose all methods.
|
|
|
99
103
|
### Usage
|
|
100
104
|
|
|
101
105
|
```bash
|
|
102
|
-
curl -X POST https://
|
|
106
|
+
curl -X POST https://{app-subdomain}.mindstudio.ai/_/methods/submit-vendor-request/invoke \
|
|
103
107
|
-H "Authorization: Bearer sk..." \
|
|
104
108
|
-H "Content-Type: application/json" \
|
|
105
109
|
-d '{ "input": { "name": "Acme" } }'
|
|
@@ -200,7 +204,7 @@ Inbound HTTP endpoints that invoke methods.
|
|
|
200
204
|
}
|
|
201
205
|
```
|
|
202
206
|
|
|
203
|
-
Endpoint URL: `https://
|
|
207
|
+
Endpoint URL: `https://{app-subdomain}.mindstudio.ai/_/webhook/{secret}`
|
|
204
208
|
|
|
205
209
|
Accepts any HTTP method. The method receives `{ method, headers, query, body }` as input.
|
|
206
210
|
|
|
@@ -38,7 +38,9 @@ Always consult the code sanity check before writing code in initialCodegen with
|
|
|
38
38
|
|
|
39
39
|
### QA (`runAutomatedBrowserTest`)
|
|
40
40
|
|
|
41
|
-
For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app.
|
|
41
|
+
For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app.
|
|
42
|
+
|
|
43
|
+
The QA agent can see the screen. Describe what to test, not how — it will figure out what to click, what to check, and what values to use.
|
|
42
44
|
|
|
43
45
|
### Background Execution
|
|
44
46
|
|