fix: cursor agent workspace from config instead of type assertion

Address review feedback: remove unsafe `as unknown as` cast on
currentRole. CursorAgentConfig now takes workspace directly instead
of using ExtractFn to infer it from thread context.

Refs #180
This commit is contained in:
2026-05-11 08:00:51 +00:00
parent 904ee1eb83
commit fe87efd79d
4 changed files with 10 additions and 30 deletions
@@ -1,23 +1,12 @@
import { describe, expect, test } from "bun:test";
import type { ExtractFn } from "@uncaged/workflow-runtime";
import type * as z from "zod/v4";
import { createCursorAgent, validateCursorAgentConfig } from "../src/index.js";
const testExtract: ExtractFn = async <T extends Record<string, unknown>>(
_schema: z.ZodType<T>,
_contentHash: string,
): Promise<{ meta: T; contentPayload: string; refs: string[] }> => ({
meta: { workspace: "/tmp" } as unknown as T,
contentPayload: "",
refs: [],
});
describe("validateCursorAgentConfig", () => {
test("accepts valid config", () => {
const r = validateCursorAgentConfig({
model: null,
timeout: 0,
extract: testExtract,
workspace: "/tmp/test-project",
});
expect(r.ok).toBe(true);
});
@@ -26,11 +15,11 @@ describe("validateCursorAgentConfig", () => {
const r = validateCursorAgentConfig({
model: null,
timeout: 0,
extract: null as unknown as ExtractFn,
workspace: "",
});
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toContain("extract");
expect(r.error).toContain("workspace");
}
});
@@ -38,7 +27,7 @@ describe("validateCursorAgentConfig", () => {
const r = validateCursorAgentConfig({
model: null,
timeout: -1,
extract: testExtract,
workspace: "/tmp/test-project",
});
expect(r.ok).toBe(false);
});
@@ -49,7 +38,7 @@ describe("createCursorAgent", () => {
const agent = createCursorAgent({
model: null,
timeout: 0,
extract: testExtract,
workspace: "/tmp/test-project",
});
expect(typeof agent).toBe("function");
});
@@ -59,7 +48,7 @@ describe("createCursorAgent", () => {
createCursorAgent({
model: null,
timeout: -1,
extract: testExtract,
workspace: "/tmp/test-project",
}),
).toThrow();
});
+1 -8
View File
@@ -1,6 +1,5 @@
import type { AgentFn } from "@uncaged/workflow-runtime";
import { buildAgentPrompt, type SpawnCliError, spawnCli } from "@uncaged/workflow-util-agent";
import * as z from "zod/v4";
import type { CursorAgentConfig } from "./types.js";
import { validateCursorAgentConfig } from "./validate-config.js";
@@ -8,12 +7,6 @@ import { validateCursorAgentConfig } from "./validate-config.js";
export type { CursorAgentConfig } from "./types.js";
export { validateCursorAgentConfig } from "./validate-config.js";
const cursorWorkspaceSchema = z.object({
workspace: z
.string()
.describe("Absolute path to the project/repository directory the agent should work in"),
});
function throwCursorSpawnError(error: SpawnCliError): never {
if (error.kind === "non_zero_exit") {
throw new Error(
@@ -44,7 +37,7 @@ export function createCursorAgent(config: CursorAgentConfig): AgentFn {
const timeoutMs = config.timeout > 0 ? config.timeout : null;
return async (ctx) => {
const { workspace } = ctx.currentRole as unknown as { workspace: string };
const workspace = config.workspace;
const fullPrompt = await buildAgentPrompt(ctx);
const args = [
"-p",
+1 -3
View File
@@ -1,7 +1,5 @@
import type { ExtractFn } from "@uncaged/workflow-runtime";
export type CursorAgentConfig = {
model: string | null;
timeout: number;
extract: ExtractFn;
workspace: string;
};
@@ -3,8 +3,8 @@ import { err, ok, type Result } from "@uncaged/workflow-runtime";
import type { CursorAgentConfig } from "./types.js";
export function validateCursorAgentConfig(config: CursorAgentConfig): Result<void, string> {
if (typeof config.extract !== "function") {
return err("extract must be a function");
if (typeof config.workspace !== "string" || config.workspace.length === 0) {
return err("workspace must be a non-empty string (absolute path)");
}
if (config.timeout < 0) {
return err("timeout must be a non-negative number (milliseconds); use 0 for no limit");