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
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Convera Agent Instructions

## Real Application Verification

For changes that affect application behavior, Electron main/preload/renderer
integration, local AI providers, MCP servers or tools, settings, or
user-visible flows, unit tests, lint, and typecheck are not sufficient by
themselves.

Before reporting the work complete:

1. Read `packages/app/automation/README.md` completely.
2. After a clean checkout or any app bundle/source change, run
`pnpm --filter @convera/app automation:prepare`.
3. Start the automation MCP with `pnpm --filter @convera/app automation` and
drive the real Electron app with `convera_session`, `convera_observe`,
`convera_interact`, and `convera_wait`. Use `convera_execute` only when the
semantic actions cannot express the required step.
4. Use an isolated `profile_id` and verify the production-facing behavior,
including visible success and the relevant failure or degraded state. Do
not substitute mocks or unit tests for this verification.
5. Inspect the actual UI state and capture a screenshot or log artifact under
the ignored `packages/app/.automation/` directory when it provides useful
evidence.
6. Close the automation session and clean up only the exact temporary
configuration and profile data created by the test. Never modify a real
user profile, session memo, or conversation database.
7. Report the exercised scenario, provider/runtime, evidence, and any remaining
real-machine boundary. For provider work, do not run real Claude unless the
user explicitly asks; use real Codex when requested or available.

If real application automation cannot run, do not silently call the feature
complete. Report the blocker and the exact unverified boundary.
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"@hurdlegroup/robotjs": "^0.12.3",
"@icons-pack/react-simple-icons": "^12.2.0",
"@leeoniya/ufuzzy": "^1.0.18",
"@modelcontextprotocol/sdk": "1.12.3",
"@modelcontextprotocol/sdk": "1.13.0",
"@radix-ui/react-accordion": "^1.2.4",
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-aspect-ratio": "^1.1.3",
Expand Down
84 changes: 84 additions & 0 deletions packages/app/src/electron/ai/__tests__/claude-code-mcp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { LocalAIChatRequest } from "@/shared/types/local-ai";
import type { LanguageModel } from "ai";
import { describe, expect, it, vi } from "vitest";
import { LOCAL_AI_PROVIDER_DESCRIPTORS } from "../provider-descriptors";
import { ClaudeCodeAdapter } from "../providers/claude-code";
import type { LocalAiProviderStatus } from "../types";

const mocks = vi.hoisted(() => {
const model = {} as LanguageModel;
const provider = vi.fn(() => model);

return {
model,
provider,
createClaudeCode: vi.fn(() => provider),
createSdkMcpServer: vi.fn(),
tool: vi.fn(),
};
});

vi.mock("ai-sdk-provider-claude-code", () => ({
createClaudeCode: mocks.createClaudeCode,
createSdkMcpServer: mocks.createSdkMcpServer,
tool: mocks.tool,
}));

describe("ClaudeCodeAdapter MCP transport", () => {
it("passes connected managed MCP servers to Claude without converting their tools", async () => {
const adapter = new ClaudeCodeAdapter();
const request: LocalAIChatRequest = {
requestId: "native-mcp",
conversationId: "conversation",
turnId: "turn",
providerId: "claude-code",
modelId: "claude-test",
operation: {
kind: "append",
message: { role: "user", content: "use cua" },
},
options: { cwd: "/tmp/convera-test" },
};
const status: LocalAiProviderStatus = {
...LOCAL_AI_PROVIDER_DESCRIPTORS["claude-code"],
available: true,
authenticated: true,
executablePath: "/test/claude",
defaultModel: "claude-test",
models: ["claude-test"],
checkedAt: new Date(0).toISOString(),
};

const run = await adapter.prepareRun(request, status, {
tools: [],
nativeMcpServers: {
cua: {
transport: "stdio",
command: "cua-driver",
args: ["mcp"],
toolNames: ["screenshot"],
},
},
requestInteraction: vi.fn(async () => ({ approved: false })),
});

expect(run.model).toBe(mocks.model);
expect(mocks.provider).toHaveBeenCalledWith(
"claude-test",
expect.objectContaining({
pathToClaudeCodeExecutable: "/test/claude",
cwd: "/tmp/convera-test",
mcpServers: {
cua: {
type: "stdio",
command: "cua-driver",
args: ["mcp"],
},
},
allowedTools: ["mcp__cua__screenshot"],
}),
);
expect(mocks.createSdkMcpServer).not.toHaveBeenCalled();
expect(mocks.tool).not.toHaveBeenCalled();
});
});
Loading
Loading