Merge mainline and record final Cursor installed-release qualification

Resolve the two Runner fixture conflicts while preserving selected native response waits. Record all 18 passing local/Daytona cells, five original failed attempts, immutable package/image identities and clean installation evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-06 10:14:43 -05:00
commit 67aff7ad16
101 files changed
+2592 -255

No files matched your search

+2
View File
@@ -16,6 +16,8 @@ export interface RunForIssue {
invocationSource: string;
responsibleUserId?: string | null;
errorCode?: string | null;
/** Bounded provider message for a failed model rejection. */
error?: string | null;
usageJson: Record<string, unknown> | null;
resultJson: Record<string, unknown> | null;
logBytes?: number | null;
@@ -107,6 +107,28 @@ describe("stopped task recovery notice", () => {
expect(onRetried).toHaveBeenCalledOnce();
});
it.each([
"The 'gpt-6.1-sol' model is not supported when using Codex with a ChatGPT account.",
"The selected model is not supported by the current ChatGPT connection. Choose a supported model or a compatible AI connection.",
])("shows the model rejection and repair action without expanding run logs: %s", async (runError) => {
vi.mocked(activityApi.runsForIssue).mockResolvedValue([{ runId: "model-run", agentId: "agent",
status: "failed", errorCode: "native_provider_model_rejected" }] as never);
await act(async () => root.render(<QueryClientProvider client={client}>
<ExecutionBlockerNotice companyId="company" issueId="model-task" onRetried={onRetried} blocker={{
recoveryActionId: "recovery", runId: "model-run", agentId: "agent",
cause: "native_continuation_requires_reconciliation", canRetry: true,
runError,
nextAction: "Inspect the original failure before continuing.",
}} />
</QueryClientProvider>));
await act(async () => { await new Promise(resolve => setTimeout(resolve, 10)); });
expect(container.textContent).toContain("Model unavailable.");
expect(container.textContent).toContain(runError);
expect(container.textContent).toContain("clear the task's model override, then retry");
expect(container.textContent).toContain("Inspect the original failure before continuing.");
expect(container.querySelector("button")?.textContent).toBe("Retry");
});
it.each(["native_continuation_requires_reconciliation", "uncertain_external_action"])("offers Retry for a server-admitted native failure: %s", async cause => {
await act(async () => root.render(<QueryClientProvider client={client}>
<ExecutionBlockerNotice companyId="company" issueId="task" onRetried={onRetried} blocker={{
+3 -1
View File
@@ -19,6 +19,7 @@ export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried
});
const failedRun = runs?.find(run => run.runId === blocker.runId &&
["failed", "timed_out"].includes(run.status));
const modelRejected = failedRun?.errorCode === "native_provider_model_rejected";
const requiresInspection = blocker.cause === "native_continuation_requires_reconciliation" ||
blocker.cause === "native_session_cleanup_quarantined";
const retry = useMutation({
@@ -34,7 +35,8 @@ export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried
return (
<div role="status" aria-label="Task recovery" className="mx-(--sz-execution-blocker-inline) my-(--sz-execution-blocker-block) flex flex-wrap items-center justify-between execution-blocker-notice border border-border bg-muted text-foreground">
<div className="min-w-0 flex-1 break-words">
<p>Recovery needed.{blocker.runError ? ` ${blocker.runError}` : ""}</p>
<p>{modelRejected ? "Model unavailable." : "Recovery needed."}{blocker.runError ? ` ${blocker.runError}` : ""}</p>
{modelRejected && <p>Choose a supported model or clear the task's model override, then retry.</p>}
<p>{blocker.nextAction}</p>
{Boolean(blocker.savedMessageCount) && (
<p>{blocker.savedMessageCount} saved {blocker.savedMessageCount === 1 ? "message is" : "messages are"} waiting for recovery.</p>
+26
View File
@@ -1803,6 +1803,32 @@ describe("TaskChatThread runtime transcript selection", () => {
expect(container.textContent).not.toContain("The runner stopped");
});
it.each([
"The 'gpt-6.1-sol' model is not supported when using Codex with a ChatGPT account.",
"The selected model is not supported by the current ChatGPT connection. Choose a supported model or a compatible AI connection.",
])("promotes a rejected model above the runner's generated failure response: %s", (error) => {
nativeTranscriptState.transcriptByRun.set("model-rejected", [{
kind: "run_result", ts: "2026-08-25T18:00:01.000Z",
summary: "The Codex run failed before it completed.",
disposition: "needs_review", objectiveSatisfied: false, verification: [],
remainingWork: [], blocker: null, artifacts: [],
}]);
render(<TaskChatThread comments={[]} onAdd={async () => {}} linkedRuns={[{
runId: "model-rejected", runtimeMode: "native", status: "failed",
errorCode: "native_provider_model_rejected",
error,
agentId: "agent-1", agentName: "Runner", adapterType: "paperclip_runner",
createdAt: "2026-08-25T18:00:00.000Z", startedAt: "2026-08-25T18:00:00.000Z",
finishedAt: "2026-08-25T18:00:02.000Z",
}]} />);
const marker = container.querySelector('[data-testid="task-chat-collapsible-marker"]');
expect(marker?.textContent).toContain("Model unavailable");
flushSync(() => marker!.querySelector<HTMLButtonElement>('button[aria-expanded="false"]')!.click());
expect(container.textContent).toContain(error);
expect(container.textContent).toContain("clear the task's model override, then retry");
expect(container.textContent).not.toContain("after returning a final response");
});
it("keeps workspace contention out of the conversation's cancellation markers", () => {
render(<TaskChatThread comments={[]} onAdd={async () => {}} linkedRuns={[{
runId: "workspace-wait", runtimeMode: "native", status: "cancelled", errorCode: "workspace_busy",
+4 -2
View File
@@ -1637,7 +1637,9 @@ export function TaskChatThread(props: TaskChatThreadProps) {
? "native_runner_timed_out"
: "native_runner_process_exited");
const label =
code === "native_provider_approval_required" && source.status === "failed"
code === "native_provider_model_rejected" && source.status === "failed"
? "Model unavailable"
: code === "native_provider_approval_required" && source.status === "failed"
? "Approval required"
: code === "native_provider_usage_limit" && source.status === "failed"
? "Usage limit reached"
@@ -1659,7 +1661,7 @@ export function TaskChatThread(props: TaskChatThreadProps) {
: code === "native_provider_approval_required"
? "This operation requires approval, but this runner has no interactive approval handler. Review the operation and update the agent's permission setting before retrying."
: code === "native_provider_model_rejected"
? "The provider rejected the selected model. Check the model ID and your account's access, save the agent configuration, then retry. View the run for the provider's full error."
? `${meta?.error || "The provider rejected the selected model."} Choose a supported model or clear the task's model override, then retry.`
: code === "native_provider_usage_limit" &&
source.status === "failed"
? "The model provider has reached its current usage limit. Try again after the limit resets."
+1
View File
@@ -59,6 +59,7 @@ export interface IssueChatLinkedRun {
hasStoredOutput?: boolean;
logBytes?: number | null;
errorCode?: string | null;
error?: string | null;
scheduledRetryAt?: string | null;
nextAction?: string | null;
resultJson?: Record<string, unknown> | null;