Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
- Fixed OMP requests to OpenAI-compatible providers failing with `Invalid schema for function 'browser_handoff'`. Codex and DeepSeek reject a tool whose root parameters schema is a union, so OMP runs that forward extension schemas unnormalized failed on every request. `browser_handoff` and `browser_credentials` now advertise one strict object, as `browser_open` and `browser_snapshot` already did; Core still enforces the fields each operation or action requires. Pi schemas are unchanged.
- Fixed `browser_open` returning a page revision that was already stale. Chrome starts loading a created tab after the tab exists, and that load advanced the revision right after `browser_open` returned, so the first `browser_act` with the returned revision failed with `stale_page_revision`. `browser_open` now returns after the first load completes, up to 10 seconds, and reports the loaded page's revision and URL.
- Made failed `browser_act` batches report their progress. The error `details` now carry `failed_action_index` and the `completed_actions` results, and in multi-action batches the message names the failing step and how many steps completed before it. A failure after a completed step now reports `outcome: "unknown"` instead of `not_started`, because the earlier steps already changed the page.
- Fixed the OMP adapter's tool schemas lagging behind the protocol. OMP now offers `hover`, `double_click`, `context_click`, press `modifiers`, `frame_id`, `expect_download`, dialog `prompt_text`, snapshot `frame_id` and `match`, and the download-cursor, selector-state, and value waits that the Pi adapter already accepted, and rejects duplicate `keep_tab_ids` as Pi does. A parity test now fails when the OMP schemas accept less than the Pi schemas.
Expand Down
57 changes: 24 additions & 33 deletions packages/omp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,45 +283,36 @@ const DEFINITIONS: ReadonlyArray<{
label: "Browser Handoff",
description: "Request user attention for credentials, MFA, CAPTCHA, or other human-only input. Non-blocking: post a notice, tell the user in chat, then verify the page yourself and resolve or dismiss it by notice_id.",
approval: "write",
schema: (z) => z.union([
z.object({
operation: z.literal("request"),
tab_id: z.number().int().min(0),
expected_page_revision: z.number().int().min(0),
prompt: z.string().min(1).max(2000),
completion: z.object({
kind: z.enum(["url", "selector"]),
value: z.string().min(1).max(65_536),
}).strict().optional(),
timeout_ms: z.number().int().min(1000).max(900_000).optional(),
}).strict(),
z.object({
operation: z.enum(["status", "resolve", "dismiss"]),
notice_id: z.string().min(1).max(256),
}).strict(),
]),
// One flat object covers every operation: OpenAI-compatible providers reject a root union.
Comment thread
wolfiesch marked this conversation as resolved.
// Core enforces which fields each operation requires.
schema: (z) => z.object({
operation: z.enum(["request", "status", "resolve", "dismiss"]),
tab_id: z.number().int().min(0).optional(),
expected_page_revision: z.number().int().min(0).optional(),
prompt: z.string().min(1).max(2000).optional(),
completion: z.object({
kind: z.enum(["url", "selector"]),
value: z.string().min(1).max(65_536),
}).strict().optional(),
timeout_ms: z.number().int().min(1000).max(900_000).optional(),
notice_id: z.string().min(1).max(256).optional(),
}).strict(),
},
{
name: "browser_credentials",
label: "Browser Credentials",
description: "Prefer this before human handoff: use a URL-matching 1Password Login without exposing its values. Prepare first; more than three matches require the user. Fill only, then submit through Browser Act.",
approval: "write",
schema: (z) => z.union([
z.object({
action: z.literal("prepare"),
tab_id: z.number().int().min(0),
expected_page_revision: z.number().int().min(0),
}).strict(),
z.object({
action: z.enum(["fill", "next"]),
tab_id: z.number().int().min(0),
expected_page_revision: z.number().int().min(0),
credential_token: z.string().min(32).max(256),
username_ref: z.string().min(1).max(256).optional(),
password_ref: z.string().min(1).max(256).optional(),
otp_ref: z.string().min(1).max(256).optional(),
}).strict(),
]),
// One flat object covers every action; Core enforces credential_token for fill and next.
schema: (z) => z.object({
action: z.enum(["prepare", "fill", "next"]),
tab_id: z.number().int().min(0),
expected_page_revision: z.number().int().min(0),
credential_token: z.string().min(32).max(256).optional(),
username_ref: z.string().min(1).max(256).optional(),
password_ref: z.string().min(1).max(256).optional(),
otp_ref: z.string().min(1).max(256).optional(),
}).strict(),
},
{
name: "browser_commit",
Expand Down
10 changes: 5 additions & 5 deletions packages/omp/test/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,12 @@ test("Standard OMP mode registers the complete Core RPC tool surface", () => {
});


test("Standard read and open tools expose provider-compatible object schemas", () => {
const registered = register(false);
test("every OMP tool exposes a provider-compatible root object schema", () => {
const registered = register(true);
for (const tool of registered.tools) {
expect({ tool: tool.name, parameters: tool.parameters }).toMatchObject({ tool: tool.name, parameters: { kind: "object" } });
}
const open = registered.tools.find((tool) => tool.name === "browser_open");
const snapshot = registered.tools.find((tool) => tool.name === "browser_snapshot");
expect(open?.parameters).toMatchObject({ kind: "object" });
expect(snapshot?.parameters).toMatchObject({ kind: "object" });
expect(stubState(open?.parameters).vocabulary.values).toContain("new_window");
});

Expand Down
Loading