Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import {
type RunInspectDeps,
} from "@/routes/v2/pages/Tangent/services/createWorkareaRemoteTools";
import { createRemoteEnvAgentWorker } from "@/routes/v2/pages/Tangent/services/remoteEnvAgentWorker";
import { createRemoteEnvHost } from "@/routes/v2/pages/Tangent/services/remoteEnvHost";
import {
createRemoteEnvHost,
describeBrowserEnv,
} from "@/routes/v2/pages/Tangent/services/remoteEnvHost";
import { createDebugBridgeHandlers } from "@/routes/v2/shared/components/AiChat/toolBridge/debugBridge";
import { createRunBridgeHandlers } from "@/routes/v2/shared/components/AiChat/toolBridge/runBridge";
import type { BridgeDeps } from "@/routes/v2/shared/components/AiChat/toolBridge/utils";
Expand Down Expand Up @@ -292,6 +295,7 @@ export function TangentProjectAgentProvider({
tools,
sessionId: activeSessionId,
agentTargets,
metadata: describeBrowserEnv({ mode: "editor" }),
});

const stop = connectRemoteEnvWithRefresh({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import { useRemoteEnvAuthToken } from "@/routes/v2/pages/Tangent/hooks/useRemote
import { useTangentBaseUrl } from "@/routes/v2/pages/Tangent/hooks/useTangentBaseUrl";
import { connectRemoteEnvWithRefresh } from "@/routes/v2/pages/Tangent/services/connectRemoteEnvWithRefresh";
import { createRemoteEnvAgentWorker } from "@/routes/v2/pages/Tangent/services/remoteEnvAgentWorker";
import { createRemoteEnvHost } from "@/routes/v2/pages/Tangent/services/remoteEnvHost";
import {
createRemoteEnvHost,
describeBrowserEnv,
} from "@/routes/v2/pages/Tangent/services/remoteEnvHost";

interface TangentRemoteEnvProviderProps {
sessionId: string;
Expand Down Expand Up @@ -135,6 +138,7 @@ export function TangentRemoteEnvProvider({
url: baseUrl,
worker: remote,
onError,
metadata: describeBrowserEnv(contextRef.current),
});

const stop = connectRemoteEnvWithRefresh({
Expand Down
49 changes: 48 additions & 1 deletion src/routes/v2/pages/Tangent/services/remoteEnvHost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { RemoteEnvWorkerApi } from "@/agent/createRemoteEnvWorkerApi";
import type { ToolBridgeApi } from "@/agent/toolBridgeApi";

import type { AgentTargetRouter } from "./createActiveTabRoutingBridge";
import { createRemoteEnvHost } from "./remoteEnvHost";
import { createRemoteEnvHost, describeBrowserEnv } from "./remoteEnvHost";

const connectRemoteEnvironment = vi.fn();

Expand Down Expand Up @@ -139,6 +139,37 @@ describe("createRemoteEnvHost", () => {
await expect(connection).resolves.toBeUndefined();
});

it("forwards host metadata into the connect options", () => {
const worker = makeWorker();
const metadata = {
editor: "browser",
description: "Tangent browser canvas",
};
const host = createRemoteEnvHost({
url: "http://localhost:8000",
worker: worker as unknown as Remote<RemoteEnvWorkerApi>,
metadata,
});

void host.connect("token", "env-1");

const options = connectRemoteEnvironment.mock.calls.at(-1)?.[0];
expect(options.metadata).toEqual(metadata);
});

it("omits metadata from the connect options when unset", () => {
const worker = makeWorker();
const host = createRemoteEnvHost({
url: "http://localhost:8000",
worker: worker as unknown as Remote<RemoteEnvWorkerApi>,
});

void host.connect("token", "env-1");

const options = connectRemoteEnvironment.mock.calls.at(-1)?.[0];
expect(options.metadata).toBeUndefined();
});

it("settles a connect that a disconnect superseded", async () => {
const worker = makeWorker();
const host = createRemoteEnvHost({
Expand Down Expand Up @@ -594,3 +625,19 @@ describe("createRemoteEnvHost", () => {
expect(onError).toHaveBeenCalledWith("kill failed");
});
});

describe("describeBrowserEnv", () => {
it("describes the editor host", () => {
const metadata = describeBrowserEnv({ mode: "editor" });
expect(metadata.editor).toBe("browser");
expect(metadata.description).toBe("Tangent browser canvas");
expect(metadata.capabilities).toContain("edits the pipeline spec");
});

it("describes the run-view host distinctly", () => {
const metadata = describeBrowserEnv({ mode: "runView", runId: "r1" });
expect(metadata.editor).toBe("browser");
expect(metadata.description).toBe("Tangent browser run inspector");
expect(metadata.capabilities).toContain("read-only");
});
});
25 changes: 24 additions & 1 deletion src/routes/v2/pages/Tangent/services/remoteEnvHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type { Remote } from "comlink";
import { proxy } from "comlink";

import type { RemoteEnvWorkerApi } from "@/agent/createRemoteEnvWorkerApi";
import type { AgentContext } from "@/agent/types";
import type { AgentTargetRouter } from "@/routes/v2/pages/Tangent/services/createActiveTabRoutingBridge";
import { getTangentSocketConfig } from "@/routes/v2/pages/Tangent/services/socketConfig";
import { isRecord } from "@/utils/typeGuards";
Expand Down Expand Up @@ -146,6 +147,26 @@ interface RemoteEnvHostBaseOptions {
worker: Remote<RemoteEnvWorkerApi>;
onError?: (message: string) => void;
agentTargets?: AgentTargetRouter;
metadata?: Record<string, string>;
}

// Self-description Prime reads via `list_environments` to tell this browser host
// apart from an editor host (e.g. the VS Code extension) and target it.
export function describeBrowserEnv(
context: AgentContext,
): Record<string, string> {
if (context.mode === "runView") {
return {
editor: "browser",
description: "Tangent browser run inspector",
capabilities: "inspects a run's executions (read-only)",
};
}
return {
editor: "browser",
description: "Tangent browser canvas",
capabilities: "edits the pipeline spec live on the browser canvas",
};
}

interface RemoteEnvToolHostOptions {
Expand Down Expand Up @@ -175,7 +196,8 @@ interface AgentLifecycle {
export function createRemoteEnvHost(
options: RemoteEnvHostOptions,
): RemoteEnvHost {
const { url, worker, onError, tools, sessionId, agentTargets } = options;
const { url, worker, onError, tools, sessionId, agentTargets, metadata } =
options;

let client: RemoteEnvironmentClient | null = null;
const agentLifecycles = new Map<string, AgentLifecycle>();
Expand Down Expand Up @@ -402,6 +424,7 @@ export function createRemoteEnvHost(
environmentId,
handlers,
...(loggedTools ? { tools: loggedTools, sessionId } : {}),
...(metadata ? { metadata } : {}),
});
client = nextClient;
socketHolder.socket = nextClient.socket;
Expand Down
Loading