Skip to content

Add Meta Muse Spark computer-use provider#59

Open
rgarcia wants to merge 15 commits into
mainfrom
hypeship/add-meta-muse-spark
Open

Add Meta Muse Spark computer-use provider#59
rgarcia wants to merge 15 commits into
mainfrom
hypeship/add-meta-muse-spark

Conversation

@rgarcia

@rgarcia rgarcia commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add meta:muse-spark-1.1 with Meta Model API authentication and OpenAI-compatible Responses streaming
  • continue tool loops through previous_response_id, keep GUI actions serial, and remove encrypted reasoning replay where Meta rejects it
  • expose normalized 0–1000 computer tools through the existing provider-neutral runtime
  • document the provider and extend model discovery, API integration, agent, harness, and CLI coverage

Testing

  • npm run typecheck
  • npm test --workspace @onkernel/cua-ai (149 tests)
  • npm test --workspace @onkernel/cua-agent (160 tests; live matrix skipped in the unit run)
  • npm test --workspace @onkernel/cua-cli (88 tests; TUI fixtures skipped)
  • live Meta model discovery and function-tool smoke
  • live two-turn screenshot tool loop verifying previous_response_id
  • live CuaAgent and CuaAgentHarness browser smokes
  • live CLI navigation against example.com
  • provider-owned cookbook audit and documentation drift check

npm run build completed the AI, agent, TypeScript, and CLI build steps; the unrelated ptywright native step could not run because Zig is unavailable locally.


Note

Medium Risk
Touches provider routing and multi-turn Responses threading (shared with OpenAI), which can affect tool-loop behavior; scope is a new provider path with substantial test coverage rather than changes to existing provider defaults.

Overview
Adds Meta as a first-class CUA provider for meta:muse-spark-1.1 (Muse Spark 1.1), using the OpenAI-compatible Responses API at https://api.meta.ai/v1 with META_API_KEY instead of a native computer tool.

The new meta-responses stream adapter reuses pi-ai’s Responses transport but applies Meta-specific request rules: store: true, previous_response_id threading for multi-turn tool loops, parallel_tool_calls: false, and stripping include: reasoning.encrypted_content (incompatible with threaded requests). OpenAI’s threading logic is factored into shared threadResponsesRequest in common.ts; Meta wraps it with threadMetaRequest.

Runtime wiring registers Meta in the model catalog (CUA_MODEL_ANNOTATIONS, CUA_MODEL_OVERRIDES), routes models to META_RESPONSES_API, and exposes canonical computer tools with a 0–1000 normalized coordinate system (computer mode only). Agent responseThreading docs and behavior now include Meta alongside OpenAI and Tzafon.

Docs, CLI (cua models -p meta, env vars), update-models discovery/smoke/drift scripts, and CI integration/e2e jobs gain Meta coverage; tests cover threading payloads and live screenshot loops when keys are present.

Reviewed by Cursor Bugbot for commit 6bcb1c5. Bugbot is set up for automated code reviews on this repo. Configure here.

@rgarcia rgarcia marked this pull request as ready for review July 11, 2026 20:21

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Integration screenshot path needs cwd
    • I replaced the single cwd-based path with a resolver that finds examples/screenshot.png from either packages/ai or monorepo-root test runs.
  • ✅ Fixed: Meta tests ignore MODEL_API_KEY
    • I added a shared Meta API key helper using META_MODEL_API_KEY ?? MODEL_API_KEY and used it for both gating and request auth in Meta integration tests.

Create PR

Or push these changes by commenting:

@cursor push 4874c204f2
Preview (4874c204f2)
diff --git a/packages/ai/test/computer-tool.integration.test.ts b/packages/ai/test/computer-tool.integration.test.ts
--- a/packages/ai/test/computer-tool.integration.test.ts
+++ b/packages/ai/test/computer-tool.integration.test.ts
@@ -1,5 +1,6 @@
+import { existsSync } from "node:fs";
 import { readFile } from "node:fs/promises";
-import { join } from "node:path";
+import { dirname, join } from "node:path";
 import { describe, expect, it } from "vitest";
 import {
 	CUA_ACTION_TYPES,
@@ -16,8 +17,30 @@
 	yutori,
 } from "../src/index";
 
-const screenshotPath = join(process.cwd(), "examples", "screenshot.png");
+function resolveScreenshotPath(): string {
+	let currentDir = process.cwd();
 
+	while (true) {
+		const localPath = join(currentDir, "examples", "screenshot.png");
+		if (existsSync(localPath)) {
+			return localPath;
+		}
+
+		const monorepoPath = join(currentDir, "packages", "ai", "examples", "screenshot.png");
+		if (existsSync(monorepoPath)) {
+			return monorepoPath;
+		}
+
+		const parentDir = dirname(currentDir);
+		if (parentDir === currentDir) {
+			return monorepoPath;
+		}
+		currentDir = parentDir;
+	}
+}
+
+const screenshotPath = resolveScreenshotPath();
+
 interface ProviderCase {
 	provider: CuaProvider;
 	envVar: string;
@@ -26,9 +49,13 @@
 	coordinateRange: readonly [number, number];
 	requireToolCalls: boolean;
 	ciOptInEnvVar?: string;
+	getApiKey?: () => string | undefined;
 	extraOptions?: Record<string, unknown>;
 }
 
+const getMetaApiKey = (): string | undefined =>
+	process.env.META_MODEL_API_KEY ?? process.env.MODEL_API_KEY;
+
 const cases: ProviderCase[] = [
 	{
 		provider: "openai",
@@ -62,6 +89,7 @@
 		tools: () => meta.computerTools({ actions: ["click"] }),
 		coordinateRange: [0, 1000],
 		requireToolCalls: true,
+		getApiKey: getMetaApiKey,
 	},
 	{
 		provider: "tzafon",
@@ -114,7 +142,8 @@
 
 describe("individual computer action integration", () => {
 	for (const c of cases) {
-		const hasKey = !!process.env[c.envVar];
+		const apiKey = c.getApiKey?.() ?? process.env[c.envVar];
+		const hasKey = !!apiKey;
 		const ciEnabled = !c.ciOptInEnvVar || !process.env.CI || process.env[c.ciOptInEnvVar] === "1";
 		const test = hasKey ? it : it.skip;
 
@@ -122,7 +151,7 @@
 			const model = getCuaModel(c.modelRef as never);
 			const context = await buildContext(c.tools);
 			const response = await cuaModels().complete(model, context, {
-				apiKey: process.env[c.envVar],
+				apiKey,
 				maxTokens: 1024,
 				...c.extraOptions,
 			});
@@ -149,7 +178,8 @@
 		}, 60_000);
 	}
 
-	const metaHasKey = !!process.env.META_MODEL_API_KEY;
+	const metaApiKey = getMetaApiKey();
+	const metaHasKey = !!metaApiKey;
 	(metaHasKey ? it : it.skip)(
 		"meta continues a screenshot tool loop with previous_response_id",
 		async () => {
@@ -171,7 +201,7 @@
 				tools,
 			};
 			const first = await cuaModels().complete(model, context, {
-				apiKey: process.env.META_MODEL_API_KEY,
+				apiKey: metaApiKey,
 				maxTokens: 1024,
 			});
 			const click = first.content.find((part) => part.type === "toolCall" && part.name === "click");
@@ -191,7 +221,7 @@
 
 			let payload: Record<string, unknown> | undefined;
 			const second = await cuaModels().complete(model, context, {
-				apiKey: process.env.META_MODEL_API_KEY,
+				apiKey: metaApiKey,
 				maxTokens: 1024,
 				onPayload: (value) => {
 					payload = value as Record<string, unknown>;

You can send follow-ups to the cloud agent here.

Comment thread packages/ai/test/computer-tool.integration.test.ts Outdated
Comment thread packages/ai/test/computer-tool.integration.test.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: AGENTS loader omits MODEL_API_KEY
    • Updated the AGENTS.md key-loading loop to include MODEL_API_KEY so Meta credentials load when only the official alias is set.
  • ✅ Fixed: Missing TSDoc on buildMetaSystemPrompt
    • Added a TSDoc block describing buildMetaSystemPrompt purpose and the optional suffix behavior to satisfy exported API documentation rules.

Create PR

Or push these changes by commenting:

@cursor push 1144551363
Preview (1144551363)
diff --git a/.agents/skills/update-models/SKILL.md b/.agents/skills/update-models/SKILL.md
--- a/.agents/skills/update-models/SKILL.md
+++ b/.agents/skills/update-models/SKILL.md
@@ -16,7 +16,7 @@
 eval "$(python3 - <<'PY'
 import pathlib, re, shlex
 text = pathlib.Path('~/AGENTS.md').expanduser().read_text()
-for key in ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'GOOGLE_API_KEY', 'META_MODEL_API_KEY', 'TZAFON_API_KEY', 'YUTORI_API_KEY']:
+for key in ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'GOOGLE_API_KEY', 'META_MODEL_API_KEY', 'MODEL_API_KEY', 'TZAFON_API_KEY', 'YUTORI_API_KEY']:
     m = re.search(r'export\s+' + re.escape(key) + r'=(?:"([^"]+)"|([^\s\n]+))', text)
     if m:
         print(f'export {key}={shlex.quote(m.group(1) or m.group(2))}')

diff --git a/packages/ai/src/providers/meta/index.ts b/packages/ai/src/providers/meta/index.ts
--- a/packages/ai/src/providers/meta/index.ts
+++ b/packages/ai/src/providers/meta/index.ts
@@ -29,6 +29,12 @@
 
 export const META_COMPUTER_INSTRUCTIONS = `You control a Kernel cloud browser through individual browser tools. Coordinates are normalized from 0 to 1000 relative to the most recent screenshot, with (0, 0) at the top left and (1000, 1000) at the bottom right. Base each action on the latest observed state and request a screenshot when you need a fresh view.`;
 
+/**
+ * Builds Meta's default system prompt for computer-use runs.
+ *
+ * The optional suffix lets callers append run-specific instructions while
+ * preserving the shared base guidance for tool usage and coordinates.
+ */
 export function buildMetaSystemPrompt(opts: { suffix?: string } = {}): string {
 	return [META_COMPUTER_INSTRUCTIONS, opts.suffix].filter(Boolean).join("\n\n");
 }

You can send follow-ups to the cloud agent here.

Comment thread .agents/skills/update-models/SKILL.md Outdated
Comment thread packages/ai/src/providers/meta/index.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Meta smoke screenshot path
    • Updated smokeMeta to resolve screenshot.png from either package-local or repo-root paths before reading it, preventing cwd-dependent failures.

Create PR

Or push these changes by commenting:

@cursor push bf7648b5f2
Preview (bf7648b5f2)
diff --git a/.agents/skills/update-models/reference/discover-models.ts b/.agents/skills/update-models/reference/discover-models.ts
--- a/.agents/skills/update-models/reference/discover-models.ts
+++ b/.agents/skills/update-models/reference/discover-models.ts
@@ -1,4 +1,5 @@
 #!/usr/bin/env tsx
+import { existsSync } from "node:fs";
 import { readFile, writeFile } from "node:fs/promises";
 import { homedir } from "node:os";
 import { join } from "node:path";
@@ -201,7 +202,12 @@
 
 async function smokeMeta(client: any, model: string): Promise<SmokeResult> {
 	try {
-		const screenshot = await readFile(join(process.cwd(), "packages", "ai", "examples", "screenshot.png"));
+		const screenshotPath = [
+			join(process.cwd(), "examples", "screenshot.png"),
+			join(process.cwd(), "packages", "ai", "examples", "screenshot.png"),
+		].find(existsSync);
+		if (!screenshotPath) throw new Error("could not find packages/ai/examples/screenshot.png");
+		const screenshot = await readFile(screenshotPath);
 		const response = await client.responses.create({
 			model,
 			store: false,

You can send follow-ups to the cloud agent here.

Comment thread .agents/skills/update-models/reference/discover-models.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Missing Meta API key aliases
    • Meta credential resolution now accepts META_API_KEY, META_MODEL_API_KEY, and MODEL_API_KEY in both runtime API-key helpers and the Meta model discovery smoke path, with tests covering fallback order.

Create PR

Or push these changes by commenting:

@cursor push eb0ea9e80c
Preview (eb0ea9e80c)
diff --git a/.agents/skills/update-models/reference/discover-models.ts b/.agents/skills/update-models/reference/discover-models.ts
--- a/.agents/skills/update-models/reference/discover-models.ts
+++ b/.agents/skills/update-models/reference/discover-models.ts
@@ -179,7 +179,7 @@
 
 async function discoverMeta(args: Args): Promise<Record<string, unknown>> {
 	const OpenAI = await importDefault("openai", "OpenAI");
-	const apiKey = process.env.META_API_KEY;
+	const apiKey = process.env.META_API_KEY || process.env.META_MODEL_API_KEY || process.env.MODEL_API_KEY;
 	const client = new OpenAI({ apiKey, baseURL: "https://api.meta.ai/v1" });
 	const rawModels = await collectAsync(client.models.list());
 	const models: ModelResult[] = rawModels.map((m) => ({

diff --git a/packages/ai/src/api-keys.ts b/packages/ai/src/api-keys.ts
--- a/packages/ai/src/api-keys.ts
+++ b/packages/ai/src/api-keys.ts
@@ -12,7 +12,7 @@
 	openai: ["OPENAI_API_KEY"],
 	anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
 	google: ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
-	meta: ["META_API_KEY"],
+	meta: ["META_API_KEY", "META_MODEL_API_KEY", "MODEL_API_KEY"],
 	tzafon: ["TZAFON_API_KEY"],
 	yutori: ["YUTORI_API_KEY"],
 };

diff --git a/packages/ai/test/api-keys.test.ts b/packages/ai/test/api-keys.test.ts
--- a/packages/ai/test/api-keys.test.ts
+++ b/packages/ai/test/api-keys.test.ts
@@ -13,6 +13,8 @@
 	"GOOGLE_API_KEY",
 	"GEMINI_API_KEY",
 	"META_API_KEY",
+	"META_MODEL_API_KEY",
+	"MODEL_API_KEY",
 	"TZAFON_API_KEY",
 	"YUTORI_API_KEY",
 ] as const;
@@ -32,7 +34,7 @@
 		expect(cuaApiKeyEnvVarsForProvider("openai")).toEqual(["OPENAI_API_KEY"]);
 		expect(cuaApiKeyEnvVarsForProvider("google")).toEqual(["GOOGLE_API_KEY", "GEMINI_API_KEY"]);
 		expect(cuaApiKeyEnvVarsForProvider("gemini")).toEqual(["GOOGLE_API_KEY", "GEMINI_API_KEY"]);
-		expect(cuaApiKeyEnvVarsForProvider("meta")).toEqual(["META_API_KEY"]);
+		expect(cuaApiKeyEnvVarsForProvider("meta")).toEqual(["META_API_KEY", "META_MODEL_API_KEY", "MODEL_API_KEY"]);
 		expect(cuaApiKeyEnvVarsForProvider("unknown")).toEqual([]);
 	});
 
@@ -42,6 +44,15 @@
 		expect(getCuaEnvApiKey("google")).toBe("gemini");
 		process.env.GOOGLE_API_KEY = "google";
 		expect(getCuaEnvApiKey("google")).toBe("google");
+
+		delete process.env.META_API_KEY;
+		delete process.env.META_MODEL_API_KEY;
+		process.env.MODEL_API_KEY = "model";
+		expect(getCuaEnvApiKey("meta")).toBe("model");
+		process.env.META_MODEL_API_KEY = "meta-model";
+		expect(getCuaEnvApiKey("meta")).toBe("meta-model");
+		process.env.META_API_KEY = "meta";
+		expect(getCuaEnvApiKey("meta")).toBe("meta");
 	});
 
 	it("resolves keys from model refs", () => {

You can send follow-ups to the cloud agent here.

Comment thread packages/ai/src/api-keys.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Include strip before payload hooks
    • Meta payload sanitization now removes include after caller onPayload hooks run, and a regression test covers hooks that reintroduce it during threaded requests.

Create PR

Or push these changes by commenting:

@cursor push 0c3d56c5f2
Preview (0c3d56c5f2)
diff --git a/packages/ai/src/providers/meta/provider.ts b/packages/ai/src/providers/meta/provider.ts
--- a/packages/ai/src/providers/meta/provider.ts
+++ b/packages/ai/src/providers/meta/provider.ts
@@ -39,7 +39,10 @@
 		};
 		// CUA uses stored response state instead of stateless encrypted reasoning replay.
 		delete next.include;
-		return options?.onPayload ? ((await options.onPayload(next, model)) ?? next) : next;
+		const prepared = options?.onPayload ? ((await options.onPayload(next, model)) ?? next) : next;
+		const sanitized = { ...(prepared as Record<string, unknown>) };
+		delete sanitized.include;
+		return sanitized;
 	};
 	return { context: messages === context.messages ? context : { ...context, messages }, onPayload };
 }

diff --git a/packages/ai/test/meta-threading.test.ts b/packages/ai/test/meta-threading.test.ts
--- a/packages/ai/test/meta-threading.test.ts
+++ b/packages/ai/test/meta-threading.test.ts
@@ -75,4 +75,15 @@
 			},
 		});
 	});
+
+	it("strips include after caller payload hooks run", async () => {
+		const { onPayload } = threadMetaRequest(multiTurnContext(), {
+			onPayload: (payload) => ({ ...payload, include: ["reasoning.encrypted_content"] }),
+		});
+		expect(await onPayload({}, model)).toEqual({
+			store: true,
+			parallel_tool_calls: false,
+			previous_response_id: "resp_meta_1",
+		});
+	});
 });

You can send follow-ups to the cloud agent here.

Comment thread packages/ai/src/providers/meta/provider.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Harness onPayload skips Meta sanitization
    • CuaAgentHarness now re-sanitizes Meta payloads after its onPayload hook by stripping include fields, and a regression test covers this path.

Create PR

Or push these changes by commenting:

@cursor push bfd6b303c0
Preview (bfd6b303c0)
diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts
--- a/packages/agent/src/agent.ts
+++ b/packages/agent/src/agent.ts
@@ -714,8 +714,14 @@
 		}
 		this.on("before_provider_payload", async ({ model, payload }: { model: Model<Api>; payload: unknown }) => {
 			const onPayload = this.runtime.onPayload();
-			if (!onPayload) return { payload };
-			return { payload: (await onPayload(payload, model)) ?? payload };
+			const nextPayload = onPayload ? ((await onPayload(payload, model)) ?? payload) : payload;
+			if (model.provider !== "meta" || !nextPayload || typeof nextPayload !== "object") {
+				return { payload: nextPayload };
+			}
+			const sanitized = { ...(nextPayload as Record<string, unknown>) };
+			// Keep Meta requests aligned with threadMetaRequest: include is unsupported.
+			delete sanitized.include;
+			return { payload: sanitized };
 		});
 	}
 

diff --git a/packages/agent/test/agent.test.ts b/packages/agent/test/agent.test.ts
--- a/packages/agent/test/agent.test.ts
+++ b/packages/agent/test/agent.test.ts
@@ -1466,6 +1466,51 @@
 		expect(providerImageCount).toBe(4);
 	});
 
+	it("removes include from meta payloads after harness onPayload hooks", async () => {
+		const services = await createHarnessServices();
+		let observedPayload: unknown;
+		const streamFn: StreamFn = (model, _context, options) => {
+			const stream = createAssistantMessageEventStream();
+			void (async () => {
+				observedPayload = await options?.onPayload?.(
+					{
+						previous_response_id: "resp_meta_1",
+						include: ["reasoning.encrypted_content"],
+					},
+					model,
+				);
+				const message = finishMessage(createAssistantMessage(model), "done");
+				stream.push({ type: "start", partial: message });
+				stream.push({ type: "done", reason: "stop", message });
+				stream.end(message);
+			})();
+			return stream;
+		};
+		const models = createCuaModels();
+		models.setProvider({
+			id: "meta",
+			name: "meta test provider",
+			auth: { apiKey: { name: "test key", resolve: async () => ({ auth: { apiKey: "test-key" } }) } },
+			getModels: () => [],
+			stream: streamFn,
+			streamSimple: streamFn,
+		});
+		const harness = new CuaAgentHarness({
+			...services,
+			browser,
+			client,
+			model: "meta:muse-spark-1.1",
+			models,
+			onPayload: (payload) => ({ ...(payload as object), include: ["reasoning.encrypted_content"] }),
+		});
+
+		await harness.prompt("next");
+
+		expect(observedPayload).toEqual({
+			previous_response_id: "resp_meta_1",
+		});
+	});
+
 	it("applies image projection after user context hooks", async () => {
 		const run = async (replace: boolean) => {
 			const services = await createHarnessServices();

You can send follow-ups to the cloud agent here.

Comment thread packages/agent/src/agent.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Meta hooks may re-enable parallel tools
    • I updated Meta payload sanitization to force any emitted parallel_tool_calls field back to false after hooks run and added a regression test covering a hook that sets it to true.

Create PR

Or push these changes by commenting:

@cursor push c662100f7c
Preview (c662100f7c)
diff --git a/packages/ai/src/providers/meta/provider.ts b/packages/ai/src/providers/meta/provider.ts
--- a/packages/ai/src/providers/meta/provider.ts
+++ b/packages/ai/src/providers/meta/provider.ts
@@ -34,6 +34,7 @@
 		const sanitized = { ...(prepared as Record<string, unknown>) };
 		// CUA uses stored response state instead of stateless encrypted reasoning replay.
 		delete sanitized.include;
+		if ("parallel_tool_calls" in sanitized) sanitized.parallel_tool_calls = false;
 		return sanitized;
 	};
 	return { context: threaded.context, onPayload };

diff --git a/packages/ai/test/meta-threading.test.ts b/packages/ai/test/meta-threading.test.ts
--- a/packages/ai/test/meta-threading.test.ts
+++ b/packages/ai/test/meta-threading.test.ts
@@ -86,4 +86,15 @@
 			previous_response_id: "resp_meta_1",
 		});
 	});
+
+	it("keeps parallel tool calls disabled after caller payload hooks", async () => {
+		const { onPayload } = threadMetaRequest(multiTurnContext(), {
+			onPayload: (payload) => ({ ...(payload as object), parallel_tool_calls: true }),
+		});
+		expect(await onPayload({}, model)).toEqual({
+			store: true,
+			parallel_tool_calls: false,
+			previous_response_id: "resp_meta_1",
+		});
+	});
 });

You can send follow-ups to the cloud agent here.

Comment thread packages/ai/src/providers/meta/provider.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale threading TSDoc omits Meta
    • Updated CuaSimpleStreamOptions TSDoc to state that disableResponseThreading also applies to Meta requests.

Create PR

Or push these changes by commenting:

@cursor push 862a910071
Preview (862a910071)
diff --git a/packages/ai/src/providers/common.ts b/packages/ai/src/providers/common.ts
--- a/packages/ai/src/providers/common.ts
+++ b/packages/ai/src/providers/common.ts
@@ -272,7 +272,7 @@
  * pi-ai `SimpleStreamOptions` plus CUA request controls consumed by provider
  * adapters. Pass `keepToolNames` for caller tools that must survive native
  * tool-set substitution; use `disableResponseThreading` for a full-context
- * OpenAI or Tzafon request.
+ * OpenAI, Meta, or Tzafon request.
  */
 export interface CuaSimpleStreamOptions extends SimpleStreamOptions, ResponseThreadingOptions {
 	keepToolNames?: readonly string[];

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit c4e92c5. Configure here.

Comment thread packages/ai/src/providers/common.ts
@rgarcia rgarcia force-pushed the hypeship/add-meta-muse-spark branch from 35c4981 to 6bcb1c5 Compare July 13, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant