feat(loop): add worker/verifier model selection and archive support
Loop runs can now specify separate provider/model for worker and verifier agents (--provider, --model, --verify-provider, --verify-model). The --archive flag preserves agent conversation history after each iteration instead of destroying them.
This commit is contained in:
@@ -37,9 +37,15 @@ function toRows(loop: LoopRecord): InspectRow[] {
|
||||
{ key: "Status", value: loop.status },
|
||||
{ key: "Cwd", value: loop.cwd },
|
||||
{ key: "Provider", value: loop.provider },
|
||||
{ key: "Model", value: loop.model ?? "null" },
|
||||
{ key: "WorkerProvider", value: loop.workerProvider ?? "null" },
|
||||
{ key: "WorkerModel", value: loop.workerModel ?? "null" },
|
||||
{ key: "VerifierProvider", value: loop.verifierProvider ?? "null" },
|
||||
{ key: "VerifierModel", value: loop.verifierModel ?? "null" },
|
||||
{ key: "Prompt", value: loop.prompt },
|
||||
{ key: "VerifyPrompt", value: loop.verifyPrompt ?? "null" },
|
||||
{ key: "VerifyChecks", value: loop.verifyChecks.length > 0 ? loop.verifyChecks.join(" | ") : "[]" },
|
||||
{ key: "Archive", value: String(loop.archive) },
|
||||
{ key: "SleepMs", value: String(loop.sleepMs) },
|
||||
{ key: "MaxIterations", value: loop.maxIterations === null ? "null" : String(loop.maxIterations) },
|
||||
{ key: "MaxTimeMs", value: loop.maxTimeMs === null ? "null" : String(loop.maxTimeMs) },
|
||||
|
||||
@@ -18,8 +18,13 @@ export interface LoopRunRow {
|
||||
}
|
||||
|
||||
export interface LoopRunOptions extends CommandOptions {
|
||||
provider?: "claude" | "codex" | "opencode";
|
||||
model?: string;
|
||||
verifyProvider?: "claude" | "codex" | "opencode";
|
||||
verifyModel?: string;
|
||||
verify?: string;
|
||||
verifyCheck?: string[];
|
||||
archive?: boolean;
|
||||
name?: string;
|
||||
sleep?: string;
|
||||
maxIterations?: string;
|
||||
@@ -40,6 +45,10 @@ export function addLoopRunOptions(command: Command): Command {
|
||||
return command
|
||||
.description("Start a loop")
|
||||
.argument("<prompt>", "Prompt for each fresh worker iteration")
|
||||
.option("--provider <provider>", "Default provider for worker and verifier agents")
|
||||
.option("--model <model>", "Default model for worker and verifier agents")
|
||||
.option("--verify-provider <provider>", "Provider for the verifier agent")
|
||||
.option("--verify-model <model>", "Model for the verifier agent")
|
||||
.option("--verify <prompt>", "Verifier agent prompt")
|
||||
.option(
|
||||
"--verify-check <command>",
|
||||
@@ -47,6 +56,7 @@ export function addLoopRunOptions(command: Command): Command {
|
||||
collectMultiple,
|
||||
[],
|
||||
)
|
||||
.option("--archive", "Archive worker and verifier agents after each iteration")
|
||||
.option("--name <name>", "Optional loop name")
|
||||
.option("--sleep <duration>", "Delay between iterations (for example: 30s, 5m)")
|
||||
.option("--max-iterations <n>", "Maximum number of iterations")
|
||||
@@ -88,10 +98,15 @@ function buildLoopRunInput(prompt: string, options: LoopRunOptions): LoopRunInpu
|
||||
return {
|
||||
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) } : {}),
|
||||
|
||||
@@ -46,8 +46,14 @@ export interface LoopRecord {
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider: "claude" | "codex" | "opencode";
|
||||
model: string | null;
|
||||
workerProvider: "claude" | "codex" | "opencode" | null;
|
||||
workerModel: string | null;
|
||||
verifierProvider: "claude" | "codex" | "opencode" | null;
|
||||
verifierModel: string | null;
|
||||
verifyPrompt: string | null;
|
||||
verifyChecks: string[];
|
||||
archive: boolean;
|
||||
sleepMs: number;
|
||||
maxIterations: number | null;
|
||||
maxTimeMs: number | null;
|
||||
@@ -116,8 +122,15 @@ export interface LoopStopPayload {
|
||||
export interface LoopRunInput {
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider?: "claude" | "codex" | "opencode";
|
||||
model?: string;
|
||||
workerProvider?: "claude" | "codex" | "opencode";
|
||||
workerModel?: string;
|
||||
verifierProvider?: "claude" | "codex" | "opencode";
|
||||
verifierModel?: string;
|
||||
verifyPrompt?: string;
|
||||
verifyChecks?: string[];
|
||||
archive?: boolean;
|
||||
name?: string;
|
||||
sleepMs?: number;
|
||||
maxIterations?: number;
|
||||
|
||||
@@ -860,6 +860,39 @@ export class AgentManager {
|
||||
this.logger.trace({ agentId }, "closeAgent: completed");
|
||||
}
|
||||
|
||||
async archiveAgent(agentId: string): Promise<{ archivedAt: string }> {
|
||||
const agent = this.requireAgent(agentId);
|
||||
if (!this.registry) {
|
||||
throw new Error("Agent storage is not configured");
|
||||
}
|
||||
|
||||
await this.registry.applySnapshot(agent, {
|
||||
internal: agent.internal,
|
||||
});
|
||||
const stored = await this.registry.get(agentId);
|
||||
if (!stored) {
|
||||
throw new Error(`Agent ${agentId} not found in storage after snapshot`);
|
||||
}
|
||||
|
||||
const archivedAt = new Date().toISOString();
|
||||
const normalizedStatus =
|
||||
stored.lastStatus === "running" || stored.lastStatus === "initializing"
|
||||
? "idle"
|
||||
: stored.lastStatus;
|
||||
|
||||
await this.registry.upsert({
|
||||
...stored,
|
||||
archivedAt,
|
||||
lastStatus: normalizedStatus,
|
||||
requiresAttention: false,
|
||||
attentionReason: null,
|
||||
attentionTimestamp: null,
|
||||
});
|
||||
await this.closeAgent(agentId);
|
||||
|
||||
return { archivedAt };
|
||||
}
|
||||
|
||||
async setAgentMode(agentId: string, modeId: string): Promise<void> {
|
||||
const agent = this.requireAgent(agentId);
|
||||
await agent.session.setMode(modeId);
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
AgentSlashCommand,
|
||||
AgentRuntimeInfo,
|
||||
ListModelsOptions,
|
||||
AgentProvider,
|
||||
} from "./agent/agent-sdk-types.js";
|
||||
import { AgentStorage } from "./agent/agent-storage.js";
|
||||
import { AgentManager } from "./agent/agent-manager.js";
|
||||
@@ -39,10 +40,15 @@ interface ScriptedAgentBehavior {
|
||||
}
|
||||
|
||||
class ScriptedAgentClient implements AgentClient {
|
||||
readonly provider = "claude" as const;
|
||||
readonly provider: AgentProvider;
|
||||
readonly capabilities = TEST_CAPABILITIES;
|
||||
|
||||
constructor(private readonly behavior: ScriptedAgentBehavior) {}
|
||||
constructor(
|
||||
provider: AgentProvider,
|
||||
private readonly behavior: ScriptedAgentBehavior,
|
||||
) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
async isAvailable(): Promise<boolean> {
|
||||
return true;
|
||||
@@ -52,7 +58,7 @@ class ScriptedAgentClient implements AgentClient {
|
||||
config: AgentSessionConfig,
|
||||
_launchContext?: AgentLaunchContext,
|
||||
): Promise<AgentSession> {
|
||||
return new ScriptedAgentSession(config, this.behavior);
|
||||
return new ScriptedAgentSession(config, this.provider, this.behavior);
|
||||
}
|
||||
|
||||
async resumeSession(
|
||||
@@ -61,10 +67,11 @@ class ScriptedAgentClient implements AgentClient {
|
||||
): Promise<AgentSession> {
|
||||
return new ScriptedAgentSession(
|
||||
{
|
||||
provider: "claude",
|
||||
provider: this.provider,
|
||||
cwd: overrides?.cwd ?? process.cwd(),
|
||||
...overrides,
|
||||
},
|
||||
this.provider,
|
||||
this.behavior,
|
||||
);
|
||||
}
|
||||
@@ -75,7 +82,6 @@ class ScriptedAgentClient implements AgentClient {
|
||||
}
|
||||
|
||||
class ScriptedAgentSession implements AgentSession {
|
||||
readonly provider = "claude" as const;
|
||||
readonly capabilities = TEST_CAPABILITIES;
|
||||
readonly id = randomUUID();
|
||||
private readonly subscribers = new Set<(event: AgentStreamEvent) => void>();
|
||||
@@ -84,6 +90,7 @@ class ScriptedAgentSession implements AgentSession {
|
||||
|
||||
constructor(
|
||||
private readonly config: AgentSessionConfig,
|
||||
readonly provider: AgentProvider,
|
||||
private readonly behavior: ScriptedAgentBehavior,
|
||||
) {}
|
||||
|
||||
@@ -220,7 +227,7 @@ describe("LoopService", () => {
|
||||
const state = { workerRuns: 0 };
|
||||
const manager = new AgentManager({
|
||||
clients: {
|
||||
claude: new ScriptedAgentClient({
|
||||
claude: new ScriptedAgentClient("claude", {
|
||||
async onRun({ config }) {
|
||||
state.workerRuns += 1;
|
||||
if (config.title?.includes("worker") && state.workerRuns >= 2) {
|
||||
@@ -247,9 +254,7 @@ describe("LoopService", () => {
|
||||
maxIterations: 3,
|
||||
});
|
||||
|
||||
while ((await service.inspectLoop(loop.id)).status === "running") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
await waitForLoopCompletion(service, loop.id);
|
||||
|
||||
const finalLoop = await service.inspectLoop(loop.id);
|
||||
expect(finalLoop.status).toBe("succeeded");
|
||||
@@ -262,10 +267,125 @@ describe("LoopService", () => {
|
||||
expect(readFileSync(path.join(paseoHome, "loops", "loops.json"), "utf8")).toContain(loop.id);
|
||||
});
|
||||
|
||||
test("uses worker and verifier provider-model settings when provided", async () => {
|
||||
const workerConfigs: AgentSessionConfig[] = [];
|
||||
const verifierConfigs: AgentSessionConfig[] = [];
|
||||
const manager = new AgentManager({
|
||||
clients: {
|
||||
codex: new ScriptedAgentClient("codex", {
|
||||
async onRun({ config }) {
|
||||
workerConfigs.push(config);
|
||||
writeFileSync(path.join(workspaceDir, "done.txt"), "ok");
|
||||
return "done";
|
||||
},
|
||||
}),
|
||||
claude: new ScriptedAgentClient("claude", {
|
||||
async onRun({ config }) {
|
||||
verifierConfigs.push(config);
|
||||
return "{\"passed\":true,\"reason\":\"verified\"}";
|
||||
},
|
||||
}),
|
||||
},
|
||||
registry: storage,
|
||||
logger,
|
||||
});
|
||||
const service = new LoopService({ paseoHome, agentManager: manager, logger });
|
||||
await service.initialize();
|
||||
|
||||
const loop = await service.runLoop({
|
||||
prompt: "Create done.txt",
|
||||
cwd: workspaceDir,
|
||||
provider: "codex",
|
||||
model: "fallback-model",
|
||||
workerModel: "gpt-5.4",
|
||||
verifyPrompt: "Confirm that done.txt exists in the workspace.",
|
||||
verifierProvider: "claude",
|
||||
verifierModel: "sonnet",
|
||||
maxIterations: 1,
|
||||
});
|
||||
|
||||
await waitForLoopCompletion(service, loop.id);
|
||||
|
||||
const finalLoop = await service.inspectLoop(loop.id);
|
||||
expect(finalLoop.status).toBe("succeeded");
|
||||
expect(finalLoop.provider).toBe("codex");
|
||||
expect(finalLoop.model).toBe("fallback-model");
|
||||
expect(finalLoop.workerProvider).toBeNull();
|
||||
expect(finalLoop.workerModel).toBe("gpt-5.4");
|
||||
expect(finalLoop.verifierProvider).toBe("claude");
|
||||
expect(finalLoop.verifierModel).toBe("sonnet");
|
||||
expect(workerConfigs).toHaveLength(1);
|
||||
expect(workerConfigs[0]).toMatchObject({
|
||||
provider: "codex",
|
||||
model: "gpt-5.4",
|
||||
internal: true,
|
||||
});
|
||||
expect(verifierConfigs).toHaveLength(1);
|
||||
expect(verifierConfigs[0]).toMatchObject({
|
||||
provider: "claude",
|
||||
model: "sonnet",
|
||||
internal: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("archives worker and verifier agents after each iteration when requested", async () => {
|
||||
const archivedAgentIds: string[] = [];
|
||||
const manager = new AgentManager({
|
||||
clients: {
|
||||
claude: new ScriptedAgentClient("claude", {
|
||||
async onRun({ config }) {
|
||||
if (config.title?.includes("worker")) {
|
||||
writeFileSync(path.join(workspaceDir, "done.txt"), "ok");
|
||||
return "created done.txt";
|
||||
}
|
||||
return "{\"passed\":true,\"reason\":\"done.txt exists\"}";
|
||||
},
|
||||
}),
|
||||
},
|
||||
registry: storage,
|
||||
logger,
|
||||
});
|
||||
const archiveAgent = manager.archiveAgent.bind(manager);
|
||||
manager.archiveAgent = async (agentId) => {
|
||||
archivedAgentIds.push(agentId);
|
||||
await archiveAgent(agentId);
|
||||
};
|
||||
const service = new LoopService({ paseoHome, agentManager: manager, logger });
|
||||
await service.initialize();
|
||||
|
||||
const loop = await service.runLoop({
|
||||
prompt: "Create done.txt",
|
||||
cwd: workspaceDir,
|
||||
verifyPrompt: "Confirm that done.txt exists in the workspace.",
|
||||
archive: true,
|
||||
maxIterations: 1,
|
||||
});
|
||||
|
||||
await waitForLoopCompletion(service, loop.id);
|
||||
|
||||
const finalLoop = await service.inspectLoop(loop.id);
|
||||
const iteration = finalLoop.iterations[0];
|
||||
expect(finalLoop.archive).toBe(true);
|
||||
expect(iteration?.workerAgentId).toBeTruthy();
|
||||
expect(iteration?.verifierAgentId).toBeTruthy();
|
||||
expect(archivedAgentIds).toEqual([iteration!.workerAgentId!, iteration!.verifierAgentId!]);
|
||||
await storage.flush();
|
||||
await expect(storage.get(iteration!.workerAgentId!)).resolves.toMatchObject({
|
||||
id: iteration!.workerAgentId!,
|
||||
archivedAt: expect.any(String),
|
||||
internal: true,
|
||||
});
|
||||
await expect(storage.get(iteration!.verifierAgentId!)).resolves.toMatchObject({
|
||||
id: iteration!.verifierAgentId!,
|
||||
archivedAt: expect.any(String),
|
||||
internal: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("uses verifier prompt when provided", async () => {
|
||||
const manager = new AgentManager({
|
||||
clients: {
|
||||
claude: new ScriptedAgentClient({
|
||||
claude: new ScriptedAgentClient("claude", {
|
||||
async onRun({ config }) {
|
||||
if (config.title?.includes("worker")) {
|
||||
await fsMkdir(workspaceDir);
|
||||
@@ -292,9 +412,7 @@ describe("LoopService", () => {
|
||||
maxIterations: 1,
|
||||
});
|
||||
|
||||
while ((await service.inspectLoop(loop.id)).status === "running") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
await waitForLoopCompletion(service, loop.id);
|
||||
|
||||
const finalLoop = await service.inspectLoop(loop.id);
|
||||
expect(finalLoop.status).toBe("succeeded");
|
||||
@@ -313,7 +431,7 @@ describe("LoopService", () => {
|
||||
});
|
||||
const manager = new AgentManager({
|
||||
clients: {
|
||||
claude: new ScriptedAgentClient({
|
||||
claude: new ScriptedAgentClient("claude", {
|
||||
async onRun({ config }) {
|
||||
if (config.title?.includes("worker")) {
|
||||
await blocker;
|
||||
@@ -354,3 +472,9 @@ async function fsMkdir(target: string): Promise<void> {
|
||||
function pathExists(target: string): boolean {
|
||||
return existsSync(target);
|
||||
}
|
||||
|
||||
async function waitForLoopCompletion(service: LoopService, loopId: string): Promise<void> {
|
||||
while ((await service.inspectLoop(loopId)).status === "running") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,14 @@ const LoopRecordSchema = z.object({
|
||||
prompt: z.string(),
|
||||
cwd: z.string(),
|
||||
provider: z.enum(["claude", "codex", "opencode"]),
|
||||
model: z.string().nullable(),
|
||||
workerProvider: z.enum(["claude", "codex", "opencode"]).nullable(),
|
||||
workerModel: z.string().nullable(),
|
||||
verifierProvider: z.enum(["claude", "codex", "opencode"]).nullable(),
|
||||
verifierModel: z.string().nullable(),
|
||||
verifyPrompt: z.string().nullable(),
|
||||
verifyChecks: z.array(z.string()),
|
||||
archive: z.boolean(),
|
||||
sleepMs: z.number().int().nonnegative(),
|
||||
maxIterations: z.number().int().positive().nullable(),
|
||||
maxTimeMs: z.number().int().positive().nullable(),
|
||||
@@ -104,8 +110,14 @@ export interface LoopRunOptions {
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider?: AgentProvider;
|
||||
model?: string;
|
||||
workerProvider?: AgentProvider;
|
||||
workerModel?: string;
|
||||
verifierProvider?: AgentProvider;
|
||||
verifierModel?: string;
|
||||
verifyPrompt?: string;
|
||||
verifyChecks?: string[];
|
||||
archive?: boolean;
|
||||
name?: string;
|
||||
sleepMs?: number;
|
||||
maxIterations?: number;
|
||||
@@ -364,8 +376,14 @@ export class LoopService {
|
||||
prompt,
|
||||
cwd: path.resolve(input.cwd),
|
||||
provider: input.provider ?? DEFAULT_LOOP_PROVIDER,
|
||||
model: normalizePrompt(input.model, "model"),
|
||||
workerProvider: input.workerProvider ?? null,
|
||||
workerModel: normalizePrompt(input.workerModel, "workerModel"),
|
||||
verifierProvider: input.verifierProvider ?? null,
|
||||
verifierModel: normalizePrompt(input.verifierModel, "verifierModel"),
|
||||
verifyPrompt,
|
||||
verifyChecks,
|
||||
archive: input.archive ?? false,
|
||||
sleepMs: ensureNonNegativeInteger(input.sleepMs, "sleepMs"),
|
||||
maxIterations: ensurePositiveInteger(input.maxIterations, "maxIterations"),
|
||||
maxTimeMs: ensurePositiveInteger(input.maxTimeMs, "maxTimeMs"),
|
||||
@@ -634,7 +652,11 @@ export class LoopService {
|
||||
loop.updatedAt = nowIso();
|
||||
await this.persist();
|
||||
try {
|
||||
await this.options.agentManager.closeAgent(agent.id);
|
||||
if (loop.archive) {
|
||||
await this.options.agentManager.archiveAgent(agent.id);
|
||||
} else {
|
||||
await this.options.agentManager.closeAgent(agent.id);
|
||||
}
|
||||
} catch {
|
||||
// Ignore cleanup errors for internal loop workers.
|
||||
}
|
||||
@@ -745,7 +767,11 @@ export class LoopService {
|
||||
loop.updatedAt = nowIso();
|
||||
await this.persist();
|
||||
try {
|
||||
await this.options.agentManager.closeAgent(verifierAgent.id);
|
||||
if (loop.archive) {
|
||||
await this.options.agentManager.archiveAgent(verifierAgent.id);
|
||||
} else {
|
||||
await this.options.agentManager.closeAgent(verifierAgent.id);
|
||||
}
|
||||
} catch {
|
||||
// Ignore cleanup errors for internal loop verifiers.
|
||||
}
|
||||
@@ -754,8 +780,9 @@ export class LoopService {
|
||||
|
||||
private buildWorkerConfig(loop: LoopRecord, iteration: LoopIterationRecord): AgentSessionConfig {
|
||||
return {
|
||||
provider: loop.provider,
|
||||
provider: loop.workerProvider ?? loop.provider,
|
||||
cwd: loop.cwd,
|
||||
model: loop.workerModel ?? loop.model ?? undefined,
|
||||
title: buildWorkerTitle(loop, iteration.index),
|
||||
internal: true,
|
||||
};
|
||||
@@ -763,8 +790,9 @@ export class LoopService {
|
||||
|
||||
private buildVerifierConfig(loop: LoopRecord, iteration: LoopIterationRecord): AgentSessionConfig {
|
||||
return {
|
||||
provider: loop.provider,
|
||||
provider: loop.verifierProvider ?? loop.provider,
|
||||
cwd: loop.cwd,
|
||||
model: loop.verifierModel ?? loop.model ?? undefined,
|
||||
title: buildVerifierTitle(loop, iteration.index),
|
||||
internal: true,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { AgentProviderSchema } from "../agent/provider-manifest.js";
|
||||
|
||||
export const LoopLogEntrySchema = z.object({
|
||||
seq: z.number().int().positive(),
|
||||
@@ -45,9 +46,15 @@ export const LoopRecordSchema = z.object({
|
||||
name: z.string().nullable(),
|
||||
prompt: z.string(),
|
||||
cwd: z.string(),
|
||||
provider: z.enum(["claude", "codex", "opencode"]),
|
||||
provider: AgentProviderSchema,
|
||||
model: z.string().nullable(),
|
||||
workerProvider: AgentProviderSchema.nullable(),
|
||||
workerModel: z.string().nullable(),
|
||||
verifierProvider: AgentProviderSchema.nullable(),
|
||||
verifierModel: z.string().nullable(),
|
||||
verifyPrompt: z.string().nullable(),
|
||||
verifyChecks: z.array(z.string()),
|
||||
archive: z.boolean(),
|
||||
sleepMs: z.number().int().nonnegative(),
|
||||
maxIterations: z.number().int().positive().nullable(),
|
||||
maxTimeMs: z.number().int().positive().nullable(),
|
||||
@@ -80,8 +87,15 @@ export const LoopRunRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
prompt: z.string().trim().min(1),
|
||||
cwd: z.string(),
|
||||
provider: AgentProviderSchema.optional(),
|
||||
model: z.string().trim().min(1).optional(),
|
||||
workerProvider: AgentProviderSchema.optional(),
|
||||
workerModel: z.string().trim().min(1).optional(),
|
||||
verifierProvider: AgentProviderSchema.optional(),
|
||||
verifierModel: z.string().trim().min(1).optional(),
|
||||
verifyPrompt: z.string().trim().min(1).optional(),
|
||||
verifyChecks: z.array(z.string().trim().min(1)).optional(),
|
||||
archive: z.boolean().optional(),
|
||||
name: z.string().trim().min(1).optional(),
|
||||
sleepMs: z.number().int().nonnegative().optional(),
|
||||
maxIterations: z.number().int().positive().optional(),
|
||||
|
||||
@@ -7993,8 +7993,15 @@ export class Session {
|
||||
const loop = await this.loopService.runLoop({
|
||||
prompt: request.prompt,
|
||||
cwd: request.cwd,
|
||||
provider: request.provider,
|
||||
model: request.model,
|
||||
workerProvider: request.workerProvider,
|
||||
workerModel: request.workerModel,
|
||||
verifierProvider: request.verifierProvider,
|
||||
verifierModel: request.verifierModel,
|
||||
verifyPrompt: request.verifyPrompt,
|
||||
verifyChecks: request.verifyChecks,
|
||||
archive: request.archive,
|
||||
name: request.name,
|
||||
sleepMs: request.sleepMs,
|
||||
maxIterations: request.maxIterations,
|
||||
|
||||
Reference in New Issue
Block a user