Run tasks on Codex beside Claude; drop turn caps and mid-turn snapshots
This commit is contained in:
+27
-13
@@ -30,7 +30,19 @@ export type Head = {
|
||||
dispose?: () => void;
|
||||
};
|
||||
|
||||
export const models = ["claude-opus-5-5", "claude-sonnet-5-5", "claude-haiku-5-5", "claude-fable-5-1"];
|
||||
export const models = [
|
||||
"claude-opus-5-5",
|
||||
"claude-sonnet-5-5",
|
||||
"claude-haiku-5-5",
|
||||
"claude-fable-5-1",
|
||||
"gpt-6.1-sol",
|
||||
"gpt-6-astra",
|
||||
"gpt-6-sol",
|
||||
"gpt-6-luna",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-terra",
|
||||
"gpt-5.6-luna",
|
||||
];
|
||||
|
||||
const quietReply = "NO_REPLY";
|
||||
|
||||
@@ -134,7 +146,7 @@ function taskHead(rerender: () => void): Head {
|
||||
return [h("div", { class: "head-actions" }, task.status)];
|
||||
}
|
||||
const target = state.projects.find((p) => p.id === task.project_id)?.default_branch ?? "the default branch";
|
||||
const busy = sess?.state === "running" || sess?.state === "queued";
|
||||
const busy = sess?.state === "running";
|
||||
const held = task.merge_queued_at !== undefined;
|
||||
const merge = h(
|
||||
"button",
|
||||
@@ -308,7 +320,7 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
|
||||
const sess = src.session();
|
||||
const live = sess && sess.state !== "completed" ? sess : undefined;
|
||||
composerModel = live ? live.model || context.get(live.id)?.model || "" : "";
|
||||
const form = composeForm(src, key, live?.id, live?.state ?? "idle", composerModel, showPending);
|
||||
const form = composeForm(src, key, live?.id, live?.state ?? "idle", composerModel, live?.vendor, showPending);
|
||||
const old = composer.querySelector("textarea");
|
||||
const caret = old !== null && document.activeElement === old ? [old.selectionStart, old.selectionEnd] : undefined;
|
||||
replace(composer, form);
|
||||
@@ -685,10 +697,15 @@ function releaseImages(imgs: PendingImage[]): void {
|
||||
|
||||
const modelChoices = new Map<string, string>();
|
||||
|
||||
function modelSelect(key: string, current: string): HTMLSelectElement {
|
||||
const options = current && !models.includes(current) ? [current, ...models] : models;
|
||||
function vendorOf(model: string): string {
|
||||
return model.startsWith("gpt-") ? "codex" : "claude";
|
||||
}
|
||||
|
||||
function modelSelect(key: string, current: string, vendor: string | undefined): HTMLSelectElement {
|
||||
const offered = vendor ? models.filter((m) => vendorOf(m) === vendor) : models;
|
||||
const options = current && !offered.includes(current) ? [current, ...offered] : offered;
|
||||
const select = h("select", { title: "Model for the next turn" }, ...options.map((m) => h("option", { value: m }, m)));
|
||||
select.value = modelChoices.get(key) ?? (current || models[0]!);
|
||||
select.value = modelChoices.get(key) ?? (current || options[0]!);
|
||||
select.onchange = () => {
|
||||
modelChoices.set(key, select.value);
|
||||
};
|
||||
@@ -701,16 +718,13 @@ function composeForm(
|
||||
sessionId: string | undefined,
|
||||
sessionState: string,
|
||||
currentModel: string,
|
||||
vendor: string | undefined,
|
||||
showPending: (text: string, imgs: PendingImage[]) => () => void,
|
||||
): HTMLElement {
|
||||
const text = h("textarea", { placeholder: src.placeholder(sessionState), rows: 3 });
|
||||
const clearDraft = keepDraft(text, `message:${key}`);
|
||||
const send = h("button", { type: "submit", disabled: !src.canSend(sessionState) }, "Send");
|
||||
const interrupt = h(
|
||||
"button",
|
||||
{ type: "button", class: "danger", disabled: sessionState !== "running" && sessionState !== "queued" },
|
||||
sessionState === "queued" ? "Cancel queued turn" : "Interrupt",
|
||||
);
|
||||
const interrupt = h("button", { type: "button", class: "danger", disabled: sessionState !== "running" }, "Interrupt");
|
||||
interrupt.onclick = async () => {
|
||||
if (!sessionId) {
|
||||
return;
|
||||
@@ -723,7 +737,7 @@ function composeForm(
|
||||
};
|
||||
|
||||
const modelKey = `message:${sessionId ?? key}`;
|
||||
const model = modelSelect(modelKey, currentModel);
|
||||
const model = modelSelect(modelKey, currentModel, vendor);
|
||||
const form = h("form", { class: "compose" });
|
||||
const tray = imageTray(form, text, `message:${key}`);
|
||||
form.append(tray.el, text, h("div", { class: "compose-actions" }, model, interrupt, send));
|
||||
@@ -1064,7 +1078,7 @@ function messageImages(ids: unknown): HTMLElement | null {
|
||||
export async function confirmMerge(taskId: string): Promise<void> {
|
||||
const task = findTask(taskId);
|
||||
const sessionState = currentSession(taskId)?.state;
|
||||
if (!task || task.status !== "active" || task.merge_queued_at !== undefined || sessionState === "running" || sessionState === "queued") {
|
||||
if (!task || task.status !== "active" || task.merge_queued_at !== undefined || sessionState === "running") {
|
||||
return;
|
||||
}
|
||||
const target = state.projects.find((p) => p.id === task.project_id)?.default_branch ?? "the default branch";
|
||||
|
||||
Reference in New Issue
Block a user