fix(agent): defer config validation to call time
Bundle top-level code runs during `workflow add` (descriptor extraction), but agent config env vars (e.g. WORKFLOW_HERMES_COMMAND) are only available at `workflow run` time. Deferring validation prevents premature throws.
This commit is contained in:
@@ -101,27 +101,25 @@ describe("createCursorAgent", () => {
|
|||||||
expect(typeof agent).toBe("function");
|
expect(typeof agent).toBe("function");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("throws on invalid config at construction", () => {
|
test("defers validation to call time (invalid config does not throw at construction)", () => {
|
||||||
expect(() =>
|
const agent = createCursorAgent({
|
||||||
createCursorAgent({
|
command: "/usr/local/bin/cursor-agent",
|
||||||
command: "/usr/local/bin/cursor-agent",
|
model: null,
|
||||||
model: null,
|
timeout: -1,
|
||||||
timeout: -1,
|
workspace: "/tmp/test-project",
|
||||||
workspace: "/tmp/test-project",
|
llmProvider: null,
|
||||||
llmProvider: null,
|
});
|
||||||
}),
|
expect(typeof agent).toBe("function");
|
||||||
).toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("throws when null workspace without llmProvider", () => {
|
test("defers validation — null workspace without llmProvider does not throw at construction", () => {
|
||||||
expect(() =>
|
const agent = createCursorAgent({
|
||||||
createCursorAgent({
|
command: "/usr/local/bin/cursor-agent",
|
||||||
command: "/usr/local/bin/cursor-agent",
|
model: null,
|
||||||
model: null,
|
timeout: 0,
|
||||||
timeout: 0,
|
workspace: null,
|
||||||
workspace: null,
|
llmProvider: null,
|
||||||
llmProvider: null,
|
});
|
||||||
}),
|
expect(typeof agent).toBe("function");
|
||||||
).toThrow();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ function resolveCursorModel(model: string | null): string {
|
|||||||
|
|
||||||
/** Runs `cursor-agent` with workspace from config or extracted from context via LLM. */
|
/** Runs `cursor-agent` with workspace from config or extracted from context via LLM. */
|
||||||
export function createCursorAgent(config: CursorAgentConfig): AgentFn {
|
export function createCursorAgent(config: CursorAgentConfig): AgentFn {
|
||||||
const validated = validateCursorAgentConfig(config);
|
|
||||||
if (!validated.ok) {
|
|
||||||
throw new Error(validated.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelFlag = resolveCursorModel(config.model);
|
const modelFlag = resolveCursorModel(config.model);
|
||||||
const timeoutMs = config.timeout > 0 ? config.timeout : null;
|
const timeoutMs = config.timeout > 0 ? config.timeout : null;
|
||||||
const logger = createLogger({ sink: { kind: "stderr" } });
|
const logger = createLogger({ sink: { kind: "stderr" } });
|
||||||
|
|
||||||
return async (ctx) => {
|
return async (ctx) => {
|
||||||
|
const validated = validateCursorAgentConfig(config);
|
||||||
|
if (!validated.ok) {
|
||||||
|
throw new Error(validated.error);
|
||||||
|
}
|
||||||
|
|
||||||
let workspace: string;
|
let workspace: string;
|
||||||
|
|
||||||
if (config.workspace !== null) {
|
if (config.workspace !== null) {
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ describe("validateHermesAgentConfig", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("createHermesAgent", () => {
|
describe("createHermesAgent", () => {
|
||||||
test("returns an AgentFn", () => {
|
test("returns an AgentFn even with invalid config (validation deferred to call)", () => {
|
||||||
const agent = createHermesAgent({
|
const agent = createHermesAgent({
|
||||||
command: "/usr/local/bin/hermes",
|
command: "/usr/local/bin/hermes",
|
||||||
model: null,
|
model: null,
|
||||||
timeout: null,
|
timeout: -5,
|
||||||
});
|
});
|
||||||
expect(typeof agent).toBe("function");
|
expect(typeof agent).toBe("function");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ function throwHermesSpawnError(error: SpawnCliError): never {
|
|||||||
|
|
||||||
/** Runs `hermes chat` non-interactively with the Nerve-style argv contract (`-q`, `--yolo`, `--quiet`). */
|
/** Runs `hermes chat` non-interactively with the Nerve-style argv contract (`-q`, `--yolo`, `--quiet`). */
|
||||||
export function createHermesAgent(config: HermesAgentConfig): AgentFn {
|
export function createHermesAgent(config: HermesAgentConfig): AgentFn {
|
||||||
const validated = validateHermesAgentConfig(config);
|
|
||||||
if (!validated.ok) {
|
|
||||||
throw new Error(validated.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutMs = config.timeout;
|
const timeoutMs = config.timeout;
|
||||||
|
|
||||||
return async (ctx) => {
|
return async (ctx) => {
|
||||||
|
const validated = validateHermesAgentConfig(config);
|
||||||
|
if (!validated.ok) {
|
||||||
|
throw new Error(validated.error);
|
||||||
|
}
|
||||||
|
|
||||||
const fullPrompt = await buildAgentPrompt(ctx);
|
const fullPrompt = await buildAgentPrompt(ctx);
|
||||||
const args = [
|
const args = [
|
||||||
"chat",
|
"chat",
|
||||||
|
|||||||
Reference in New Issue
Block a user