Skip to content

feat(combos): add imageInput auto|disabled capability control - #1165

Open
eachann1024 wants to merge 2 commits into
lidge-jun:devfrom
eachann1024:feat/combo-image-input
Open

feat(combos): add imageInput auto|disabled capability control#1165
eachann1024 wants to merge 2 commits into
lidge-jun:devfrom
eachann1024:feat/combo-image-input

Conversation

@eachann1024

@eachann1024 eachann1024 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add per-combo imageInput: "auto" | "disabled" (default auto).
  • "disabled" strips image from published modalities and rejects image-bearing requests with HTTP 400 before dispatch.
  • Expands previous_response_id first so a continuation that only references prior images still fails closed.
  • Management API sparsifies only explicit "disabled".
  • GUI capability switch defaults on when every target supports images.

Split from #1092 per maintainer request (imageInput must not ride with the effort-picker fix).

Change graph

flowchart LR
  UI["Combo capability switch"] --> Store["OcxComboConfig.imageInput"]
  Store --> Catalog["derive strips image when disabled"]
  Store --> Expand["expand previous_response_id"]
  Expand --> Guard["reject image body HTTP 400"]
  Guard --> Dispatch["combo target dispatch"]
Loading

Screenshots

GUI capability switch (from the original #1092 verification session; same control surface):

Combo capabilities including image toggle

Verification

  • bun test tests/codex-catalog.test.ts tests/combo-management-api.test.ts tests/combo-workspace-data.test.ts tests/server-combo-failover-e2e.test.ts — 211 pass
  • bun run typecheck — exit 0

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Related: #1092

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Added automatic image-capability detection for combos based on configured targets.
    • Added an option to disable image input for text-only combos.
    • Combo configuration screens now display target capabilities and provide localized controls.
    • Image requests are rejected before processing when image input is disabled.
    • Unsupported image capabilities cannot be enabled for incompatible targets.
  • Documentation

    • Added English and Chinese guidance covering configuration, defaults, constraints, and examples.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
CodeRabbit/Codex review was requested via the review-ready label. If no review appears, comment @coderabbitai review to request one.
Maintainers: @lidge-jun @Ingwannu @Wibias

@github-actions
github-actions Bot marked this pull request as draft August 7, 2026 00:32
@github-actions github-actions Bot added the enhancement New feature or request label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 174374a3-ce91-4e82-95d1-90bbd8a0401b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Combos now support automatic or disabled image input. The server validates and rejects disallowed image requests before dispatch. The catalog, persistence layer, GUI controls, localized strings, documentation, and tests reflect the new setting.

Changes

Combo image-input support

Layer / File(s) Summary
Configuration, persistence, and catalog behavior
src/types.ts, src/combos/types.ts, src/server/management/combo-routes.ts, src/codex/catalog/aggregation.ts, tests/codex-catalog.test.ts, tests/combo-management-api.test.ts
Combo configuration accepts "auto" or "disabled", defaults to "auto", omits the default from persistence, and removes image support from published modalities when disabled.
Request validation and dispatch
src/combos/request.ts, src/combos/index.ts, src/server/responses/core.ts, tests/server-combo-failover-e2e.test.ts
Nested input_image values are detected. Expanded continuation requests are validated before child dispatch. Disabled image requests return HTTP 400 without contacting a target.
Workspace capability model and controls
gui/src/combo-capabilities.ts, gui/src/combo-workspace-data.ts, gui/src/components/combo-workspace-*.tsx, gui/src/combo-workspace-types.ts, gui/src/pages/Combos.tsx, gui/src/i18n/*.ts, gui/src/styles-combos-workspace.css, tests/combo-workspace-data.test.ts
The workspace parses model modalities, computes target image support, tracks imageInput, exposes controls in add and detail forms, persists disabled settings, and adds localized UI text and styling.
Configuration and behavior documentation
docs-site/src/content/docs/guides/combos.md, docs-site/src/content/docs/reference/configuration/routing.md, docs-site/src/content/docs/zh-cn/guides/combos.md, docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
English and Chinese documentation describes automatic modality intersection, disabled image input, request rejection, defaults, and configuration constraints.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ComboResponseHandler
  participant ImageInputDetector
  participant ComboTarget

  Client->>ComboResponseHandler: Submit combo request
  ComboResponseHandler->>ComboResponseHandler: Expand previous response state
  ComboResponseHandler->>ImageInputDetector: Inspect expanded request body
  ImageInputDetector-->>ComboResponseHandler: Return image-input presence
  ComboResponseHandler-->>Client: Return HTTP 400 when image input is disabled
  ComboResponseHandler->>ComboTarget: Dispatch expanded request when allowed
Loading

Possibly related PRs

Suggested reviewers: ingwannu, wibias, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding per-combo imageInput control with auto and disabled modes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gui/src/components/combo-workspace-types.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import the canonical ModelOption type instead of redefining it.

gui/src/components/combo-workspace-types.ts exports ModelOption, but gui/src/pages/Combos.tsx declares a separate, identical local type. This PR had to add inputModalities?: string[] by hand in both places (and, per the codebase graph, in several other files too). A future field addition can silently drift out of sync in one of the copies.

  • gui/src/components/combo-workspace-types.ts#L11-17: keep this as the single canonical ModelOption export.
  • gui/src/pages/Combos.tsx#L24-24: replace the local type ModelOption = { ... } declaration with import type { ModelOption } from "../components/combo-workspace-types";.
♻️ Proposed fix
-type ModelOption = { provider: string; id: string; namespaced?: string; reasoningEfforts?: string[]; inputModalities?: string[] };
+import type { ModelOption } from "../components/combo-workspace-types";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/combo-workspace-types.ts` at line 1, Use the canonical
ModelOption type exported by combo-workspace-types.ts as the single source of
truth. In Combos.tsx, remove the local ModelOption declaration and import the
type from ../components/combo-workspace-types, preserving all existing usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/combo-capabilities.ts`:
- Around line 5-12: Update comboImagesSupported so incomplete targets are not
filtered out before evaluation: require every target to have nonblank provider
and model values and a matching model advertising image input, while preserving
false for an empty target list. Add a regression test covering one
vision-capable target alongside one incomplete target and verify the result is
false.

In `@src/server/management/combo-routes.ts`:
- Around line 125-134: Separate management API response objects from
runtime-normalized combo objects in the GET /api/combos flow and the PUT
response path. Ensure both responses omit imageInput when it is "auto" and
include it only when explicitly "disabled", while preserving normalized values
for runtime and persistence. Update the affected test expectation in
combo-management-api.test.ts accordingly.

In `@src/server/responses/core.ts`:
- Around line 994-1007: Update the continuation handling around
expandPreviousResponseInput and previousResponseReplayFailure so an unresolved
previous_response_id is rejected in this handler before image policy validation
or child dispatch; ensure image-bearing stored continuations are expanded and
still fail when imageInput is disabled, and add regressions covering both that
case and an unavailable previous_response_id.

In `@src/types.ts`:
- Around line 844-849: Update the normalizedCombo() fixture in
tests/codex-catalog.test.ts to include the required imageInput property, using
the normalized default value expected by NormalizedComboConfig. Preserve the
fixture’s existing object spread and other fields so it remains type-correct.

In `@tests/combo-workspace-data.test.ts`:
- Around line 437-468: Add regression coverage for the disabled image-input
state: test that parseComboList preserves "disabled", draftEquals treats
"disabled" and "auto" as different, and toPutBody emits imageInput: "disabled"
only for disabled drafts while omitting the default auto value. Anchor the tests
to the existing parseComboList, draftEquals, and toPutBody symbols and retain
the current auto-state expectations.

---

Outside diff comments:
In `@gui/src/components/combo-workspace-types.ts`:
- Line 1: Use the canonical ModelOption type exported by
combo-workspace-types.ts as the single source of truth. In Combos.tsx, remove
the local ModelOption declaration and import the type from
../components/combo-workspace-types, preserving all existing usages.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c1cddfda-f280-47e8-9b07-d59f09001dce

📥 Commits

Reviewing files that changed from the base of the PR and between b39eecf and 4849dde.

📒 Files selected for processing (29)
  • docs-site/src/content/docs/guides/combos.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/zh-cn/guides/combos.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
  • gui/src/combo-capabilities.ts
  • gui/src/combo-workspace-data.ts
  • gui/src/components/combo-workspace-add-modal.tsx
  • gui/src/components/combo-workspace-controls.tsx
  • gui/src/components/combo-workspace-detail-panel.tsx
  • gui/src/components/combo-workspace-types.ts
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Combos.tsx
  • gui/src/styles-combos-workspace.css
  • src/codex/catalog/aggregation.ts
  • src/combos/index.ts
  • src/combos/request.ts
  • src/combos/types.ts
  • src/server/management/combo-routes.ts
  • src/server/responses/core.ts
  • src/types.ts
  • tests/codex-catalog.test.ts
  • tests/combo-management-api.test.ts
  • tests/combo-workspace-data.test.ts
  • tests/server-combo-failover-e2e.test.ts

Comment thread gui/src/combo-capabilities.ts
Comment thread src/server/management/combo-routes.ts
Comment thread src/server/responses/core.ts
Comment thread src/types.ts
Comment thread tests/combo-workspace-data.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4849dde79f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const targetRoute = routeModel(config, `${pick.target.provider}/${pick.target.model}`);
const childBody = concreteComboRequestBody(
rawBody,
body,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid replaying combo continuations twice

When a combo request includes previous_response_id, this now passes an already-expanded body to the child request while leaving previous_response_id in place; the child handleResponsesInner reads that JSON and calls expandPreviousResponseInput again, so every remembered input item is prepended twice before the adapter parses it. This corrupts combo continuations and inflates context/tokens; strip previous_response_id after the parent expansion or only use the expanded body for the image check before dispatching.

Useful? React with 👍 / 👎.

Comment thread gui/src/combo-capabilities.ts Outdated
if (complete.length === 0) return false;
return complete.every((target) => {
const model = models.find(
(row) => row.provider === target.provider.trim() && row.id === target.model.trim(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor forward-provider aliases when checking image support

For a combo target that uses a chatgpt/ChatGPT-forward provider, the target picker can show GPT models from the openai catalog row, but this exact provider match cannot find that row and reports imagesSupported=false. In that dashboard path, even vision-capable GPT targets show the image capability as unavailable, so users cannot turn on imageInput: "disabled" from the UI for those combos; reuse the same provider-key expansion as the picker or pass provider metadata into this check.

AGENTS.md reference: gui/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.

Comment thread src/combos/request.ts
if (record.type === "input_image") return true;
return Object.values(record).some(visit);
};
return visit(body);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit the image scan to request input

With imageInput disabled this walks the entire request object, not just the actual input being sent to the model. A valid text-only Responses request that carries metadata or a tool schema/example containing { type: "input_image" } is therefore rejected with the combo image error even though no image input would be dispatched; scan only the expanded input content instead of the whole body.

Useful? React with 👍 / 👎.

Per-combo image/multimodal policy: default auto keeps the target
intersection; disabled strips image from catalog modalities and rejects
image-bearing requests (including previous_response_id expansions) with
HTTP 400 before dispatch. Management API sparsifies only explicit
disabled. GUI exposes a capability switch when every target supports
images.
Fail closed for incomplete targets in comboImagesSupported; omit default
imageInput auto from management GET/PUT responses; reject unresolved
previous_response_id when images are disabled; cover disabled draft
persistence and normalizedCombo fixture.
@eachann1024
eachann1024 force-pushed the feat/combo-image-input branch from 01694ef to 1beda09 Compare August 7, 2026 01:31
@eachann1024
eachann1024 marked this pull request as ready for review August 7, 2026 01:31
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The PR readiness gate passed. I will review the changes.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@github-actions The readiness gate passed. I will review the pull request.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] The readiness gate is complete. The pull request is ready for review.

You are interacting with an AI system.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/guides/combos.md`:
- Line 267: Update the imageInput descriptions to use capability-intersection
wording: in docs-site/src/content/docs/guides/combos.md:267, state that "auto"
publishes image support only when every target supports images; apply the
equivalent automatic intersection rule in
docs-site/src/content/docs/reference/configuration/routing.md:66; use the same
meaning in Chinese at docs-site/src/content/docs/zh-cn/guides/combos.md:217 and
docs-site/src/content/docs/zh-cn/reference/configuration/routing.md:60. Do not
imply an "enabled" mode or add a separate validation rule.

In `@src/server/responses/core.ts`:
- Around line 1021-1029: Update the continuation check around
expandPreviousResponseInput and unresolvedPrevious so a non-empty
previous_response_id is considered unresolved only when expansion returned the
original body, not when it returned a successfully expanded continuation.
Preserve rejection for missing or corrupt state, allow stored text-only
continuations when combo.imageInput is disabled, and add a regression verifying
the target receives the expanded continuation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5febc4dc-fec4-4f69-a801-bcfe96737d52

📥 Commits

Reviewing files that changed from the base of the PR and between 20c5bd2 and 1beda09.

📒 Files selected for processing (29)
  • docs-site/src/content/docs/guides/combos.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/zh-cn/guides/combos.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
  • gui/src/combo-capabilities.ts
  • gui/src/combo-workspace-data.ts
  • gui/src/components/combo-workspace-add-modal.tsx
  • gui/src/components/combo-workspace-controls.tsx
  • gui/src/components/combo-workspace-detail-panel.tsx
  • gui/src/components/combo-workspace-types.ts
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Combos.tsx
  • gui/src/styles-combos-workspace.css
  • src/codex/catalog/aggregation.ts
  • src/combos/index.ts
  • src/combos/request.ts
  • src/combos/types.ts
  • src/server/management/combo-routes.ts
  • src/server/responses/core.ts
  • src/types.ts
  • tests/codex-catalog.test.ts
  • tests/combo-management-api.test.ts
  • tests/combo-workspace-data.test.ts
  • tests/server-combo-failover-e2e.test.ts

| `strategy` | No | `"failover"` | `"failover"` or `"round-robin"`. |
| `stickyLimit` | No | `1` | Integer from 1 to 100 successful requests per round-robin selection. |
| `defaultEffort` | No | `null` | `low`, `medium`, `high`, `xhigh`, `max`, or `ultra`; applied only when the caller omits effort and the target advertises support. |
| `imageInput` | No | `"auto"` | `"auto"` or `"disabled"`. `"disabled"` drops image from published modalities and rejects image-bearing requests before dispatch. Cannot enable image when a target lacks it. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use capability-intersection wording in all configuration tables.

imageInput has no "enabled" value. "auto" derives image support from the intersection of target modalities, while "disabled" forces text-only behavior. The current wording can imply an unsupported configuration mode or an additional validation rule.

  • docs-site/src/content/docs/guides/combos.md#L267-L267: State that "auto" publishes image support only when every target supports images.
  • docs-site/src/content/docs/reference/configuration/routing.md#L66-L66: Replace the enablement wording with the automatic intersection rule.
  • docs-site/src/content/docs/zh-cn/guides/combos.md#L217-L217: Use the same capability-intersection wording in Chinese.
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md#L60-L60: Use the same capability-intersection wording in the localized reference table.
📍 Affects 4 files
  • docs-site/src/content/docs/guides/combos.md#L267-L267 (this comment)
  • docs-site/src/content/docs/reference/configuration/routing.md#L66-L66
  • docs-site/src/content/docs/zh-cn/guides/combos.md#L217-L217
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md#L60-L60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs-site/src/content/docs/guides/combos.md` at line 267, Update the
imageInput descriptions to use capability-intersection wording: in
docs-site/src/content/docs/guides/combos.md:267, state that "auto" publishes
image support only when every target supports images; apply the equivalent
automatic intersection rule in
docs-site/src/content/docs/reference/configuration/routing.md:66; use the same
meaning in Chinese at docs-site/src/content/docs/zh-cn/guides/combos.md:217 and
docs-site/src/content/docs/zh-cn/reference/configuration/routing.md:60. Do not
imply an "enabled" mode or add a separate validation rule.

Source: Path instructions

Comment on lines +1021 to +1029
const unresolvedPrevious = typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
&& (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
if (combo.imageInput === "disabled" && unresolvedPrevious) {
return formatErrorResponse(
400,
"previous_response_not_found",
"Continuation state is unavailable or corrupt; resend the full conversation without previous_response_id.",
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not reject successfully expanded continuations.

expandPreviousResponseInput() preserves previous_response_id on its successful expanded object. Lines 1021-1022 therefore set unresolvedPrevious for both missing state and successfully materialized state. An image-disabled combo rejects every continuation, including a continuation whose prior input contains only text.

Treat the ID as unresolved only when expansion returned the original body. Add a regression with a stored text-only response and verify that the target receives the expanded continuation.

Proposed fix
-  const unresolvedPrevious = typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
+  const unresolvedPrevious = body === rawBody
+    && typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
     && (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const unresolvedPrevious = typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
&& (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
if (combo.imageInput === "disabled" && unresolvedPrevious) {
return formatErrorResponse(
400,
"previous_response_not_found",
"Continuation state is unavailable or corrupt; resend the full conversation without previous_response_id.",
);
}
const unresolvedPrevious = body === rawBody
&& typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
&& (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
if (combo.imageInput === "disabled" && unresolvedPrevious) {
return formatErrorResponse(
400,
"previous_response_not_found",
"Continuation state is unavailable or corrupt; resend the full conversation without previous_response_id.",
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/responses/core.ts` around lines 1021 - 1029, Update the
continuation check around expandPreviousResponseInput and unresolvedPrevious so
a non-empty previous_response_id is considered unresolved only when expansion
returned the original body, not when it returned a successfully expanded
continuation. Preserve rejection for missing or corrupt state, allow stored
text-only continuations when combo.imageInput is disabled, and add a regression
verifying the target receives the expanded continuation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1beda09f6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1021 to +1023
const unresolvedPrevious = typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
&& (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
if (combo.imageInput === "disabled" && unresolvedPrevious) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow resolved text-only continuations

When imageInput is disabled and a client sends a previous_response_id that is still present in the local replay store, expandPreviousResponseInput() returns a new expanded body but leaves previous_response_id on it. This predicate still treats that successful expansion as unresolved and returns 400 before comboRequestHasImageInput() can inspect the replayed input, so even text-only continuations through a text-only combo are impossible. Track whether expansion missed, or strip the id after successful expansion, before applying this unresolved-state rejection.

Useful? React with 👍 / 👎.

Comment thread src/types.ts
* Omitted / `"auto"` keeps automatic capability derivation (default: enabled when
* the target intersection includes image).
*/
imageInput?: "auto" | "disabled";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve imageInput through CLI combo updates

Once a combo has imageInput: "disabled" from the dashboard/API/config, a later ocx combo set cannot include this new field: the CLI builds a full replacement body in src/cli/combo.ts with targets/strategy/sticky/effort/alias only, and the PUT path normalizes an omitted imageInput back to auto. In that CLI update scenario the text-only guard is silently removed, so add a CLI flag or round-trip the existing value before replacing the combo.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant