Glance is Morph’s vision model, trained specifically to test code changes. Give it a diff and a URL—it figures out what to test and returns video, screenshots, errors, and network logs.
What You Get Back
| Output | Description |
|---|
| Video | MP4/WebM recording of the entire test session |
| Animated WebP | Embeddable in GitHub PRs, Slack, Notion, and more |
| Screenshots | Per-step snapshots for debugging |
| Errors | Console errors, exceptions, and failed assertions |
| Network logs | All HTTP requests/responses during the test |
Embed test recordings directly into your PRs, or use the SDK to integrate into your product—post results to your users’ PRs, Slack, Linear, or anywhere else.
Quick Start
import { MorphClient } from '@morphllm/morphsdk';
const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
const result = await morph.browser.execute({
url: "https://preview-abc.vercel.app",
diff: prDiff, // Glance uses the diff to decide what to test
task: "Test the changes in this PR",
recordVideo: true
});
// What you get back
console.log(result.success); // Agent's pass/fail assessment
console.log(result.result); // What it found
if (result.recordingId) {
const recording = await morph.browser.getRecording(result.recordingId);
console.log(recording.videoUrl); // Full video
console.log(recording.networkUrl); // Network logs
console.log(recording.consoleUrl); // Console/errors
// Get embeddable WebP for PRs, Slack, etc.
const { webpUrl } = await recording.getWebp({ maxSizeMb: 5 });
console.log(``); // Markdown-ready
}
Integration Options
Harness API
Use this when you want to run your own browser and use Glance as the “brain” for step-by-step decisions.
Endpoints
- Create session:
POST /harness/sessions
- Next step:
POST /harness/sessions/{session_id}/step
Flow
- Create a session with
url, diff, instructions, and your tools
- Take a screenshot in your browser
- Send the screenshot to
/step, get back tool_calls
- Execute the tool calls, take a new screenshot, repeat
// 1. Create session
const session = await fetch("https://browser.morphllm.com/harness/sessions", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
url: "https://myapp.com",
diff: prDiff,
instructions: "Test the login flow",
tools: [{ name: "click", description: "Click at coordinates", parameters: { /* ... */ } }]
})
}).then(r => r.json());
// 2. Step loop
let done = false;
while (!done) {
const screenshot = await page.screenshot({ encoding: "base64" });
const step = await fetch(`https://browser.morphllm.com/harness/sessions/${session.session_id}/step`, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ screenshot, context: { current_url: page.url() } })
}).then(r => r.json());
// Execute tool_calls in your browser
for (const call of step.tool_calls) {
if (call.name === "click") await page.mouse.click(call.args.x, call.args.y);
if (call.name === "type_text") await page.keyboard.type(call.args.text);
}
done = step.is_done;
}
Step Response
{
"step_index": 3,
"tool_calls": [{ "name": "click", "args": { "x": 942, "y": 20 } }],
"is_done": false,
"done_reason": null
}
Send client_step_id with each step for idempotency—retries won’t create duplicate steps.