Add class-based CUA agent surfaces and runtime spec - #8
Conversation
Switch cua-agent to class-first CuaAgent/CuaHarness constructors, centralize provider-specific defaults in cua-ai via resolveCuaRuntimeSpec, and add exhaustive tool coverage plus live e2e scenarios to validate browser execution across providers. Co-authored-by: Cursor <cursoragent@cursor.com>
Move provider API-key env conventions into cua-ai, make CuaAgent/CuaHarness default to readable env-key resolution, improve harness quickstarts with transcript usage, and standardize extensionless TypeScript imports with a dist rewrite step for runtime compatibility. Co-authored-by: Cursor <cursoragent@cursor.com>
Ensure example scripts compile cua-ai and cua-agent first so new package exports are available even from a clean checkout. Co-authored-by: Cursor <cursoragent@cursor.com>
Add a source condition to package exports and run example scripts with --conditions=source so workspace examples resolve TypeScript sources without requiring prebuilt dist artifacts. Co-authored-by: Cursor <cursoragent@cursor.com>
Set OpenAI runtime payloads to store response items so multi-turn agent loops can reference prior response items without 404 errors. Co-authored-by: Cursor <cursoragent@cursor.com>
Run CuaAgent/CuaHarness live smoke tests for all configured providers on non-draft PRs and main pushes, and fail on any assistant/tool errors so runtime regressions are caught automatically. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the dist import extension rewrite utility and its build hooks, restore explicit fixture entry extensions for node execution, and align CUA provider handling with latest main (google provider naming and runtime spec matching). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Firetiger deploy monitoring skipped This PR didn't match the auto-monitor filter configured on your GitHub connection:
Reason: PR modifies CUA agent packages and runtime specs, but does not appear to change API endpoints (packages/api/cmd/api/) or Temporal workflows (packages/api/lib/temporal) in the kernel repo. To monitor this PR anyway, reply with |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Accessing property on void return causes runtime crash
- Updated the harness smoke example to await
harness.prompt()and derivestopReasonfrom the latest assistant message inharness.state.messagesinstead of reading a value from a void return.
- Updated the harness smoke example to await
Or push these changes by commenting:
@cursor push 68f5011708
Preview (68f5011708)
diff --git a/packages/agent/examples/harness-openai-smoke.ts b/packages/agent/examples/harness-openai-smoke.ts
--- a/packages/agent/examples/harness-openai-smoke.ts
+++ b/packages/agent/examples/harness-openai-smoke.ts
@@ -29,8 +29,9 @@
const scenario = SCENARIOS[0]!;
console.log(`running scenario: ${scenario.name}`);
- const response = await harness.prompt(scenario.prompt);
- console.log("assistant stopReason:", response.stopReason);
+ await harness.prompt(scenario.prompt);
+ const assistant = [...harness.state.messages].reverse().find((message) => message.role === "assistant");
+ console.log("assistant stopReason:", assistant?.role === "assistant" ? assistant.stopReason : "unknown");
} finally {
await client.browsers.deleteByID(browser.session_id);
}You can send follow-ups to the cloud agent here.
Tzafon's model is non-deterministic about emitting tool calls under our test prompts; mirror the requireToolCalls guard from #8 so a no-tool-call response no longer fails CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Dropped
.jsin URL not fixed by build script- Restored
./fixture-main.jsin thenew URL(...)fixture entry path so the spawned script target is explicit and no post-build rewrite is needed.
- Restored
- ✅ Fixed: Redundant branches produce identical results in rewrite
- Collapsed the duplicate extension-preserving branches in
rewriteto a singleif (ext)path while keeping behavior unchanged.
- Collapsed the duplicate extension-preserving branches in
Or push these changes by commenting:
@cursor push d7e125664c
Preview (d7e125664c)
diff --git a/packages/cua-cli/src/tui/testing/fixture.test.ts b/packages/cua-cli/src/tui/testing/fixture.test.ts
--- a/packages/cua-cli/src/tui/testing/fixture.test.ts
+++ b/packages/cua-cli/src/tui/testing/fixture.test.ts
@@ -4,7 +4,7 @@
import { KeyCtrlC, KeyEnter, spawnSession } from "@onkernel/ptywright";
function spawnFixture() {
- const fixtureMain = fileURLToPath(new URL("./fixture-main", import.meta.url));
+ const fixtureMain = fileURLToPath(new URL("./fixture-main.js", import.meta.url));
const fixtureJson = fileURLToPath(new URL("../../../src/tui/testing/fixtures/basic.json", import.meta.url));
const cwd = fileURLToPath(new URL("../../../", import.meta.url));
return spawnSession({
diff --git a/scripts/append-js-extensions.mjs b/scripts/append-js-extensions.mjs
--- a/scripts/append-js-extensions.mjs
+++ b/scripts/append-js-extensions.mjs
@@ -24,8 +24,7 @@
function rewrite(content, re) {
return content.replace(re, (_, prefix, specifier, suffix) => {
const ext = path.extname(specifier);
- if (ext && JS_EXTENSIONS.has(ext)) return `${prefix}${specifier}${suffix}`;
- if (ext && !JS_EXTENSIONS.has(ext)) return `${prefix}${specifier}${suffix}`;
+ if (ext) return `${prefix}${specifier}${suffix}`;
return `${prefix}${specifier}.js${suffix}`;
});
}You can send follow-ups to the cloud agent here.
|
|
||
| function spawnFixture() { | ||
| const fixtureMain = fileURLToPath(new URL("./fixture-main.js", import.meta.url)); | ||
| const fixtureMain = fileURLToPath(new URL("./fixture-main", import.meta.url)); |
There was a problem hiding this comment.
Dropped .js in URL not fixed by build script
Medium Severity
The .js extension was removed from new URL("./fixture-main.js", ...) to new URL("./fixture-main", ...). The append-js-extensions.mjs build script only matches from '...' and import('...') patterns via regex — it won't rewrite a string inside new URL(...). In the compiled output, fileURLToPath will resolve to a path without .js, and since the project uses "type": "module" with Node ≥ 20, ESM entry-point resolution requires explicit extensions, so node /path/to/fixture-main will fail with MODULE_NOT_FOUND.
Reviewed by Cursor Bugbot for commit d650c27. Configure here.
| if (ext && !JS_EXTENSIONS.has(ext)) return `${prefix}${specifier}${suffix}`; | ||
| return `${prefix}${specifier}.js${suffix}`; | ||
| }); | ||
| } |
There was a problem hiding this comment.
Redundant branches produce identical results in rewrite
Low Severity
In the rewrite function, the first branch (ext && JS_EXTENSIONS.has(ext)) and second branch (ext && !JS_EXTENSIONS.has(ext)) both return the same unchanged string ${prefix}${specifier}${suffix}. These two conditionals could be collapsed into a single if (ext) check, making the intent clearer: "only append .js when there's no extension at all."
Reviewed by Cursor Bugbot for commit d650c27. Configure here.
#7) * ai: tighten public surface and document CuaProvider + supported models - Un-export parseCuaModelRef/formatCuaModelRef (internal helpers). - Add docs/supported-models.md enumerating CUA-supported models per provider with source citations, linked from README. - Add a CuaProvider section to the README explaining the type, its relationship to pi-ai's Provider, and the gemini/google rename. - Drop the now-redundant "See examples/quickstart.ts" line from the README quick start. * ai: rename gemini -> google, gate public API via named re-exports - Rename CuaProvider key from "gemini" to "google" so it matches pi-ai's Model.provider exactly. providerForModel becomes a thin isCuaProvider guard. Drops the rename map and the dead piProviderFor switch. - Switch packages/ai/src/index.ts from `export *` to named re-exports for models.ts and providers/common.ts. Keeps the public surface to getCuaModel/listCuaModels/providerForModel/isCuaProvider, the action types/input types, CUA tool name constants, CUA_ACTION_TYPES, and createComputerToolDefinitions. Internal exports (parseCuaModelRef, formatCuaModelRef, findCuaAnnotation, CUA_PROVIDERS, CUA_MODEL_ANNOTATIONS, schemas) stay reachable from tests via ../src/models.js but are no longer part of the package interface. - Restore parse/format unit tests, plus update annotation tests to the new google key. - Trim README copy: drop the registry/override caveat, drop "with source citations", and rewrite the CuaProvider section now that the rename is gone. Reword the action-vocabulary section to talk about types instead of dropped schemas. * ai: update integration test gemini ref to google * ai: stabilize tzafon batch tool integration expectations Tzafon's model is non-deterministic about emitting tool calls under our test prompts; mirror the requireToolCalls guard from #8 so a no-tool-call response no longer fails CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: rgarcia <72655+rgarcia@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
6214e3d to
ed31e65
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Default case silently applies OpenAI config to unknown providers
- Removed the OpenAI default fall-through and added an exhaustive
neverguard so newly added providers fail loudly instead of inheriting OpenAI runtime settings.
- Removed the OpenAI default fall-through and added an exhaustive
Or push these changes by commenting:
@cursor push 16a6bd22eb
Preview (16a6bd22eb)
diff --git a/packages/ai/src/runtime-spec.ts b/packages/ai/src/runtime-spec.ts
--- a/packages/ai/src/runtime-spec.ts
+++ b/packages/ai/src/runtime-spec.ts
@@ -67,7 +67,6 @@
onPayload: yutori.yutoriBuiltinToolsOnPayload,
};
case "openai":
- default:
return {
model,
provider,
@@ -76,4 +75,7 @@
onPayload: openai.openaiResponsesStoreOnPayload,
};
}
+
+ const exhaustiveProvider: never = provider;
+ throw new Error(`Unsupported CUA provider: ${exhaustiveProvider}`);
}You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit ed31e65. Configure here.
| defaultSystemPrompt: openai.OPENAI_BATCH_INSTRUCTIONS, | ||
| onPayload: openai.openaiResponsesStoreOnPayload, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Default case silently applies OpenAI config to unknown providers
Medium Severity
The case "openai": default: fall-through in resolveCuaRuntimeSpec silently applies OpenAI's full runtime spec — tool definitions, system prompt, and the new openaiResponsesStoreOnPayload middleware that forces store: true — for any provider not matched by an explicit case. While currently unreachable (since providerForModel constrains to five known values), this default clause suppresses TypeScript's exhaustiveness checking: if a sixth value is added to CuaProvider, the compiler will not flag the missing case, and the new provider would silently receive OpenAI defaults including involuntary response storage.
Reviewed by Cursor Bugbot for commit ed31e65. Configure here.



Summary
@onkernel/cua-agentAPI with class-firstCuaAgentandCuaHarnessconstructors, keeping pi-like options while making browser/client runtime requirements explicit@onkernel/cua-aiviaresolveCuaRuntimeSpec()(default tool definitions, system prompts, and optional payload middleware), and consume that spec fromcua-agentto keep runtime wiring provider-neutralpackages/agent/examplescua-aivscua-agentownership boundary indocs/architecture.mdand refresh package docs/changelogs to match the new class-first surfaceTest plan
npm run build --workspace @onkernel/cua-ainpm run test --workspace @onkernel/cua-ainpm run build --workspace @onkernel/cua-agentnpm run test --workspace @onkernel/cua-agentCUA_E2E_LIVE=1 ... npm run test --workspace @onkernel/cua-agent -- test/e2e.live.test.tsMade with Cursor
Note
Medium Risk
Medium risk because it replaces the
@onkernel/cua-agentpublic construction API and changes how default tools/prompts/payload middleware are selected, which can affect runtime behavior across providers.Overview
Introduces a class-first API in
@onkernel/cua-agentby replacing thecreateCuaAgent()factory withCuaAgent(extends piAgent) and a newCuaHarnesswrapper, makingclient/browserrequired and defaultinggetApiKeyto CUA env var conventions.Centralizes provider-specific defaults in
@onkernel/cua-aiviaresolveCuaRuntimeSpec()(tool definitions, default system prompts, and payload middleware like Yutori filtering and OpenAIstore: true), and updates agent tool execution to be provider-neutral/typed with schema validation and fail-fast handling of unsupported tool names.Expands validation and CI: adds env-gated live e2e provider matrix tests for the agent package, tool-executor exhaustiveness tests, new runnable
packages/agent/examples, and updates docs/changelogs plus CI to skip integration/e2e on draft PRs while adding anagent-e2ejob.Reviewed by Cursor Bugbot for commit ed31e65. Bugbot is set up for automated code reviews on this repo. Configure here.