Merge branch 'main' of github.com:getpaseo/paseo

This commit is contained in:
Mohamed Boudra
2026-04-28 14:37:00 +07:00
3 changed files with 144 additions and 15 deletions

View File

@@ -0,0 +1,89 @@
import { describe, it, expect } from "vitest";
import { buildLoopRunInput } from "./run.js";
import type { LoopRunOptions } from "./types.js";
describe("buildLoopRunInput", () => {
it("should parse provider/model from provider string", () => {
const options: LoopRunOptions = {
provider: "opencode/hy3-preview-free",
model: undefined,
verifyProvider: undefined,
verifyModel: undefined,
verify: undefined,
verifyCheck: [],
archive: false,
name: undefined,
sleep: undefined,
maxIterations: undefined,
maxTime: undefined,
} as LoopRunOptions;
const result = buildLoopRunInput("test prompt", options);
expect(result.provider).toBe("opencode");
expect(result.model).toBe("hy3-preview-free");
});
it("should use provider only when no model in string", () => {
const options = {
provider: "opencode",
model: undefined,
verifyProvider: undefined,
verifyModel: undefined,
verify: undefined,
verifyCheck: [],
archive: false,
name: undefined,
sleep: undefined,
maxIterations: undefined,
maxTime: undefined,
} as LoopRunOptions;
const result = buildLoopRunInput("test prompt", options);
expect(result.provider).toBe("opencode");
expect(result.model).toBeUndefined();
});
it("should pass verifierProvider with model parsing", () => {
const options = {
provider: undefined,
model: undefined,
verifyProvider: "codex/gpt-5.4",
verifyModel: undefined,
verify: undefined,
verifyCheck: [],
archive: false,
name: undefined,
sleep: undefined,
maxIterations: undefined,
maxTime: undefined,
} as LoopRunOptions;
const result = buildLoopRunInput("test prompt", options);
expect(result.verifierProvider).toBe("codex");
expect(result.verifierModel).toBe("gpt-5.4");
});
it("should prefer explicit model over parsed model", () => {
const options = {
provider: "opencode/hy3-preview-free",
model: "other-model",
verifyProvider: undefined,
verifyModel: undefined,
verify: undefined,
verifyCheck: [],
archive: false,
name: undefined,
sleep: undefined,
maxIterations: undefined,
maxTime: undefined,
} as LoopRunOptions;
const result = buildLoopRunInput("test prompt", options);
expect(result.provider).toBe("opencode");
expect(result.model).toBe("other-model");
});
});

View File

@@ -8,6 +8,7 @@ import type {
} from "../../output/index.js";
import { collectMultiple } from "../../utils/command-options.js";
import { parseDuration } from "../../utils/duration.js";
import { resolveProviderAndModel } from "../../utils/provider-model.js";
import type { LoopDaemonClient, LoopRecord, LoopRunInput } from "./types.js";
export interface LoopRunRow {
@@ -86,7 +87,8 @@ function parseMaxIterations(value: string | undefined): number | undefined {
return parsed;
}
function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInput {
// oxlint-disable complexity
export function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInput {
const verifyPrompt = options.verify?.trim();
if (options.verify !== undefined && !verifyPrompt) {
throw {
@@ -95,24 +97,54 @@ function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInpu
} satisfies CommandError;
}
return {
const result: LoopRunInput = {
prompt,
cwd: process.cwd(),
...(options.provider ? { provider: options.provider } : {}),
...(options.model?.trim() ? { model: options.model.trim() } : {}),
...(options.verifyProvider ? { verifierProvider: options.verifyProvider } : {}),
...(options.verifyModel?.trim() ? { verifierModel: options.verifyModel.trim() } : {}),
...(verifyPrompt ? { verifyPrompt } : {}),
...(options.verifyCheck && options.verifyCheck.length > 0
? { verifyChecks: options.verifyCheck }
: {}),
...(options.archive ? { archive: true } : {}),
...(options.name?.trim() ? { name: options.name.trim() } : {}),
...(options.sleep ? { sleepMs: parseDuration(options.sleep) } : {}),
...(options.maxIterations ? { maxIterations: parseMaxIterations(options.maxIterations) } : {}),
...(options.maxTime ? { maxTimeMs: parseDuration(options.maxTime) } : {}),
};
// Resolve provider/model
if (options.provider) {
const { provider, model } = resolveProviderAndModel({ provider: options.provider });
if (provider) result.provider = provider;
// Explicit --model takes precedence over parsed model
if (options.model?.trim()) {
result.model = options.model.trim();
} else if (model) {
result.model = model;
}
} else if (options.model?.trim()) {
result.model = options.model.trim();
}
// Resolve verifier provider/model
if (options.verifyProvider) {
const { provider, model } = resolveProviderAndModel({ provider: options.verifyProvider });
if (provider) result.verifierProvider = provider;
// Explicit --verify-model takes precedence over parsed model
if (options.verifyModel?.trim()) {
result.verifierModel = options.verifyModel.trim();
} else if (model) {
result.verifierModel = model;
}
} else if (options.verifyModel?.trim()) {
result.verifierModel = options.verifyModel.trim();
}
if (verifyPrompt) result.verifyPrompt = verifyPrompt;
if (options.verifyCheck && options.verifyCheck.length > 0) {
result.verifyChecks = options.verifyCheck;
}
if (options.archive) result.archive = true;
if (options.name?.trim()) result.name = options.name.trim();
if (options.sleep) result.sleepMs = parseDuration(options.sleep);
if (options.maxIterations) {
result.maxIterations = parseMaxIterations(options.maxIterations);
}
if (options.maxTime) result.maxTimeMs = parseDuration(options.maxTime);
return result;
}
// oxlint-enable complexity
export type LoopRunResult = SingleResult<LoopRunRow>;

View File

@@ -451,6 +451,10 @@ export interface WaitForChatMessagesOptions {
export interface RunLoopOptions {
prompt: string;
cwd: string;
provider?: string;
model?: string;
verifierProvider?: string;
verifierModel?: string;
verifyPrompt?: string | null;
verifyChecks?: string[];
name?: string | null;
@@ -3589,6 +3593,10 @@ export class DaemonClient {
type: "loop/run",
prompt: options.prompt,
cwd: options.cwd,
...(options.provider ? { provider: options.provider } : {}),
...(options.model ? { model: options.model } : {}),
...(options.verifierProvider ? { verifierProvider: options.verifierProvider } : {}),
...(options.verifierModel ? { verifierModel: options.verifierModel } : {}),
...(options.verifyPrompt ? { verifyPrompt: options.verifyPrompt } : {}),
...(options.verifyChecks && options.verifyChecks.length > 0
? { verifyChecks: options.verifyChecks }