Skip to content
Open
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
11 changes: 9 additions & 2 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ OPENAI_API_KEY=
# =============================================================================
# CODE EXECUTION - CLOUD SANDBOX (Required for cloud Agent Mode)
# =============================================================================
# E2B is the cloud sandbox provider.
# CLOUD_SANDBOX_PROVIDER=e2b
# Production selects MIOSA gradually with PostHog flag
# miosa_cloud_sandbox_rollout_v1 and falls back to E2B on acquisition errors.
# MIOSA requires the promoted HackerAI template ID supplied by MIOSA. Leave
# both values blank to keep E2B-only cloud execution.
# Set an explicit provider only for local/staging smoke tests.
# CLOUD_SANDBOX_PROVIDER=miosa
MIOSA_API_KEY=
MIOSA_TEMPLATE_ID=
# MIOSA_BASE_URL=https://api.miosa.ai/api/v1
E2B_API_KEY=
E2B_TEMPLATE=terminal-agent-sandbox
# Optional EU cluster. When configured, fresh sandboxes created by Trigger
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ To use the agent locally:
2. In the Trigger.dev dashboard → your project → **Environment Variables**,
add the env vars the task needs to run (these live on the worker, not on
Vercel): `NEXT_PUBLIC_CONVEX_URL`, `CONVEX_SERVICE_ROLE_KEY`,
`OPENROUTER_API_KEY`, `OPENAI_API_KEY`, one cloud sandbox provider, plus any keys you use
`OPENROUTER_API_KEY`, `OPENAI_API_KEY`, and `E2B_API_KEY`. Add
both `MIOSA_API_KEY` and the promoted HackerAI `MIOSA_TEMPLATE_ID` only for
the MIOSA rollout or explicit MIOSA testing; E2B remains the cloud fallback.
Add any other keys you use
(`PERPLEXITY_API_KEY`, `JINA_API_KEY`, S3, etc.).
3. Start the worker in a third terminal:

Expand Down
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const customJestConfig = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
testEnvironment: "jest-environment-jsdom",
moduleNameMapper: {
"^@miosa/sdk$": "<rootDir>/node_modules/@miosa/sdk/dist/index.js",
"^jose$": "<rootDir>/__mocks__/jose.ts",
"^@workos-inc/node$": "<rootDir>/__mocks__/workos-node.ts",
"^@workos-inc/authkit-nextjs$": "<rootDir>/__mocks__/workos-authkit.ts",
Expand All @@ -34,7 +35,7 @@ const customJestConfig = {
"^convex/browser$": "<rootDir>/__mocks__/convex/browser.ts",
},
transformIgnorePatterns: [
"node_modules/(?!(uuid|@ai-sdk|ai|convex|react-hotkeys-hook|react-markdown|streamdown|remark-.*|unified|bail|is-plain-obj|trough|vfile|unist-.*|mdast-.*|micromark.*|decode-named-character-reference|character-entities|escape-string-regexp|markdown-table|property-information|hast-.*|space-separated-tokens|comma-separated-tokens|zwitch|html-void-elements|ccount|devlop|superjson)/)",
"node_modules/(?!(uuid|@miosa/sdk|@ai-sdk|ai|convex|react-hotkeys-hook|react-markdown|streamdown|remark-.*|unified|bail|is-plain-obj|trough|vfile|unist-.*|mdast-.*|micromark.*|decode-named-character-reference|character-entities|escape-string-regexp|markdown-table|property-information|hast-.*|space-separated-tokens|comma-separated-tokens|zwitch|html-void-elements|ccount|devlop|superjson)/)",
],
testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
testPathIgnorePatterns: ["/node_modules/", "/.next/", "/e2e/", "/dist/"],
Expand Down
26 changes: 26 additions & 0 deletions lib/__tests__/system-prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,32 @@ Commands run directly on the host OS "workstation" without Docker isolation. Be
expect(prompt).not.toContain(
"agent-browser is installed in the cloud sandbox",
);
expect(prompt).not.toContain("Python 3.12.11");
expect(prompt).not.toContain("Node.js 20.19.4");
expect(prompt).not.toContain("Golang 1.24.2");
});

it("does not claim E2B-only tools for MIOSA rollout sandboxes", async () => {
const prompt = await systemPrompt(
"user_123",
"agent",
"pro",
"agent-model",
null,
null,
"full_access",
false,
"miosa",
);

expect(prompt).toContain(
"The MIOSA template provides general Python and Node command execution.",
);
expect(prompt).toContain("probe with `command -v <tool>`");
expect(prompt).not.toContain("Pre-installed Pentesting Tools:");
expect(prompt).not.toContain(
"agent-browser is installed in the cloud sandbox",
);
});

it("keeps all three Agent approval mode contracts distinct", async () => {
Expand Down
5 changes: 5 additions & 0 deletions lib/ai/subagents/__tests__/sandbox-identity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import {
describe("subagent sandbox identity", () => {
it("distinguishes E2B from user-owned Centrifugo connections", () => {
const cloud = { sandboxId: "sandbox-1" } as never;
const miosa = {
sandboxKind: "miosa" as const,
sandboxId: "sandbox-2",
} as never;
const local = {
sandboxKind: "centrifugo",
getConnectionId: () => "desktop-1",
} as never;

expect(getSubagentSandboxIdentity(cloud)).toBe("e2b:sandbox-1");
expect(getSubagentSandboxIdentity(miosa)).toBe("miosa:sandbox-2");
expect(getSubagentSandboxIdentity(local)).toBe("connection:desktop-1");
expect(() =>
assertSubagentSandboxIdentity(cloud, "e2b:sandbox-1"),
Expand Down
6 changes: 5 additions & 1 deletion lib/ai/subagents/sandbox-identity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { AnySandbox } from "@/types";
import { isCentrifugoSandbox } from "@/lib/ai/tools/utils/sandbox-types";
import {
isCentrifugoSandbox,
isMiosaSandbox,
} from "@/lib/ai/tools/utils/sandbox-types";

export const getSubagentSandboxIdentity = (sandbox: AnySandbox): string => {
if (isCentrifugoSandbox(sandbox)) {
return `connection:${sandbox.getConnectionId()}`;
}
if (isMiosaSandbox(sandbox)) return `miosa:${sandbox.sandboxId}`;
return `e2b:${sandbox.sandboxId}`;
};

Expand Down
Loading