Skip to content
2 changes: 1 addition & 1 deletion devlog/_plan/260906_release_244_followups/000_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Baseline dev: af344a28eabcee09a5e04c48ab897449792719c2, version 2.44.0. Latest p
| roadmap | this unit | Lock all decade designs; docs only |
| policy | 010_policy.md | Establish truthful maintainer integration authority |
| task-input | 020_task_input.md | Shared Responses parser contract |
| task-guidance | ../260906_stateful_task_guidance/010_raw_boundary.md | Review follow-up: align stored raw guidance before Kiro resumes |
| kiro-results | 030_kiro_results.md | Consume parsed tool-result sequence |
| opaque-recovery | 040_opaque_recovery.md | Retry and terminal semantics on composed routing |
| combo-recovery | 050_combo_recovery.md | Route recoverable parsed payloads |
Expand All @@ -32,4 +33,3 @@ One work-phase is one PABCD cycle. Publish short dependency stacks; use merge co

## Evidence boundaries
#3735/#3734 are public current-SHA reports; independently inspect code, author local-pass statements remain reports. Kiro proof is recorded-log shape plus synthetic CI tests, never a live quota-consuming request. #3644 has a network A/B report and landed diagnostic #3693; do not claim a Windows runtime reproduction from mocked tests. Detailed private logs are never committed.

17 changes: 13 additions & 4 deletions devlog/_plan/260906_release_244_followups/020_task_input.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# External Codex task-input envelopes

Depends on policy; class C3. Fix public issue #3735, observed on baseline dev. Preserve the existing unpaired-tool HTTP 400 guard from #3471.
Depends on policy; class C4 for protocol admission. Fix public issue #3735, observed on baseline dev. Preserve the existing unpaired-tool HTTP 400 guard from #3471.

## Diff-level change map
- MODIFY src/responses/parser.ts at function_call_output classification before tool lookup: route only a complete external task-input envelope to an Ocx user message. Eligibility: type function_call_output, no call_id property (including inherited properties for direct parser calls), nonempty string id/name/namespace, nonempty fully representable text/image output. Do not require specific names, prefixes, namespaces or XML content. Existing standard tool results and custom_tool_call_output keep current path.
- NEW small src/responses/task-input.ts only if predicate/content conversion would make parser more complex: pure recognition returning supported Ocx user content or undefined, no request mutation/network/storage. Reuse existing content converters only when they preserve every accepted output part and reject invalid mixed arrays rather than silently drop them.
- MODIFY tests/responses/responses-parser.test.ts and/or existing malformed-content/parser-agent-message file: narrow positive and negative fixtures. If new test file necessary, MODIFY scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json.
- MODIFY src/responses/parser.ts at function_call_output classification before tool lookup: route only a complete external task-input envelope to an Ocx user message. Eligibility: type function_call_output, no call_id property (including inherited properties for direct helper calls), nonempty string id/name/namespace, nonempty fully representable text/image output. Do not require specific names, prefixes, namespaces or XML content. Existing standard tool results and custom_tool_call_output keep current path.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- NEW src/responses/task-input.ts: pure recognition returning supported Ocx user content or undefined, no request mutation/network/storage. Reuse existing content converters only when they preserve every accepted output part and reject invalid mixed arrays rather than silently drop them.
- MODIFY tests/responses/responses-parser.test.ts, tests/responses/responses-compaction-routing.test.ts and tests/responses/openai-responses-passthrough.test.ts with narrow positive/negative fixtures. No new test file or layout registry entry is needed.
- MODIFY docs-site/src/content/docs/reference/adapters.md and docs-site/src/content/docs/guides/sub-agent-surface.md and structure/04_transports-and-sidecars.md: describe external task input as user-supplied task coordination, not fabricated tool completion. Keep passthrough/compaction raw-body contracts.

Before: result-shaped external task input enters toolResult branch with undefined call id, then translated-adapter guard returns 400. After: the complete external shape enters user message with intact supported text/images; malformed/orphan tool results still fail. No secret or raw logged transcript is copied to tests.
Expand All @@ -19,3 +19,12 @@ No-op leaves current task creation unusable; configuration cannot distinguish th

## Source follow-up folded at roadmap lock
Author yrlan-montagnier (Yrlan), GitHub id 71253160: preserve Co-authored-by: Yrlan <71253160+yrlan-montagnier@users.noreply.github.com>. Posted helper may manufacture an encrypted-content-omitted marker that makes encrypted-only input look usable; reject encrypted-only and mixed opaque/unsupported input, never use placeholder text as eligibility. Keep every pre-existing #3471 regression, adding tests rather than replacing them. Add tests/responses/responses-compaction-routing.test.ts and tests/responses/openai-responses-passthrough.test.ts to explicit remote verification. Prefer a dedicated small predicate over relocating passthrough helpers unless byte-for-byte behavior is proved.

## Task-input cycle P refresh at 25c8d2b4e
The preceding D landed policy #3739 and actual Maintain/Admin settings. Issue #3735 is still open and the author has no open PR; retain the account-linked Yrlan trailer. Source parser at lines 150-160 currently recognizes only message/agent_message as the continuation conversation boundary. Compute the optional external content once near effectiveType and include a recognized envelope in that existing boundary predicate. In the function_call_output branch, clear pendingReasoning, emit a user message and continue; leave the ordinary result branch and core guard unchanged.

Concrete new leaf: src/responses/task-input.ts exports externalTaskInputContent(item: unknown): string | OcxContentPart[] | undefined. It imports only type OcxContentPart and existing isObj/inputContentParts. Require exact function_call_output, no call_id property, nonblank id/name/namespace, and a nonblank string or fully supported array. Array parts are input_text/text/output_text with string text or input_image with nonblank string image_url and optional auto/low/high/original detail. Normalize output_text to input_text before calling the existing input converter; original image detail maps to high by that converter. Require at least one nonblank text or usable image. Reject any unsupported/opaque/malformed member, invalid detail or file-id-only reference as a whole; placeholder text never establishes eligibility. Preserve accepted text bytes, order and image references; no raw-body mutation or helper relocation from passthrough.

Field chain: external JSON shape -> pure leaf validation -> parser user message + existing _continuationConversationMessageIndex -> translated adapter's existing user-content serialization. No new persisted field/schema/config. Passthrough and compact use unchanged raw body. Tests include pending reasoning reset and previous_response_id boundary index=0 for a new envelope without a replay prefix, alongside all old #3471 controls.

Dispatch: main owns new leaf, parser, endpoint/passthrough regressions and English/structure docs; a bounded worker owns only tests/responses/responses-parser.test.ts. Independent A/C reviewer reads named leaf/parser boundaries. No local tests/typecheck/build; remote ci.yml runtime/gates and existing parser/compaction/passthrough suites provide proof. Parser leaves add no core/Lab dependency. No-op/configuration cannot fix this shape; existing input converter is reused behind strict validation.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# External task input implementation

The pure task-input leaf validates the complete external envelope before using the
existing input-content converter. It accepts text and URL-backed images, rejects
partial or opaque arrays as a whole, and preserves accepted content order. The
parser uses the result for both the continuation boundary and a user turn that
clears pending reasoning. Ordinary tool results, the core call-id guard and raw
passthrough handling remain unchanged.

Existing unpaired-result regressions remain in place. Added parser cases cover
shape/content controls, original image detail, frozen input, continuation and
reasoning separation; HTTP cases exercise accepted text/images and rejected
envelopes before upstream work. A passthrough case verifies the existing raw
orphan-output behavior alongside the new parsed user representation.

The implementation preserves Yrlan's contributor attribution from the public
issue and supplied proposal. Protocol/security review and hosted CI are recorded
on the fixing PR and source-bound cycle receipt. No local test suite, typecheck,
build or live Kiro request is part of this validation.

The first hosted run exposed two invalid HTTP test stimuli: short text in an
encrypted_content slot follows the existing plaintext normalization path before
the parser. The negative fixtures now use synthetic ciphertext-shaped content
with an explicit classifier check; a separate positive control retains plaintext
slot compatibility. The 400/no-upstream assertions and production logic are unchanged.
30 changes: 30 additions & 0 deletions devlog/_plan/260906_stateful_task_guidance/000_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Stateful external-task guidance consistency

Parent PR #3743 recognizes a complete external task-input envelope as a user turn
and starts the parsed continuation boundary there. Its review identified the
remaining raw insertion predicate in collaboration.ts, which still recognizes
only ordinary user/assistant messages and agent_message. In a stateful delta,
generated guidance can therefore precede the task in parsed messages but follow
it in the stored raw input; reparsing changes the delivered order.

This C4 protocol/replay follow-up is a separate PABCD work-phase before Kiro
implementation resumes. The Kiro phase remains open with no code changes; the
goalplan gained an additional criterion and an explicit focus cursor, without
marking any unfinished task complete or weakening existing criteria.

Archetype: spec-satisfaction repair. Goal: the same conversational boundary in
parsed and raw stateful representations. Non-goals: new envelope forms, broader
tool-output repair, stateless insertion changes, auth changes or live Kiro.
Verifier: hosted ci.yml runtime/type/privacy gates and focused regression cases
in tests/codex-integration/multi-agent-compat.test.ts. No local test suite,
typecheck or build. Stop only after exact-head CI and independent review pass,
parent review is resolved and its verified head is ready for the Kiro cascade.

Resources inherit the authorized release loop: existing repository/GitHub access,
requested xai/grok-4.6 reviewers, no new credentials or purchases, no fixed model
cost cap, bounded processes and status waits. Main owns code/FSM/GitHub actions;
reviewers are read-only. Reclaim failed dispatches; no implicit phase movement.
Design and final source/CI evidence reside in this unit and the bound goalplan.

The complete implementation map is 010_raw_boundary.md. Apply the verified delta
to parent #3743, then refresh the saved Kiro branch from that parent before B.
38 changes: 38 additions & 0 deletions devlog/_plan/260906_stateful_task_guidance/010_raw_boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Align the stateful raw conversation boundary

## Exact diff map
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- MODIFY src/server/responses/collaboration.ts: import the existing pure
externalTaskInputContent helper. In isConversationalItem, recognize a complete
external task envelope with helper(item) !== undefined, alongside existing
agent_message and user/assistant message handling. Do not duplicate its shape
validator or alter statefulRawInsertionIndex's replay-prefix/fallback logic.
- MODIFY tests/codex-integration/multi-agent-compat.test.ts near injectDeveloperMessage:
stateful external envelope alone and after a leading ordinary call result must
receive guidance before the external task in both parsed context and raw input.
Reparse the stored raw body and compare role/content order. Add an expanded
replay-prefix case so historical external inputs are not selected as the new
boundary. Keep ordinary stateful protocol, compaction and guidance-dedup tests.
- MODIFY docs-site/src/content/docs/guides/sub-agent-surface.md and
structure/04_transports-and-sidecars.md: distinguish unchanged payload content
from intentional generated-guidance placement; both representations use the
same complete-envelope boundary during stateful injection.

Before: parsed [developer, user] while raw [external-envelope, developer].
After: parsed [developer, user], raw [developer, external-envelope], and reparsed
role/content order agrees. Leading protocol results remain before guidance;
historical replay-prefix items remain in place.

## Activation and boundary proof
The new predicate executes only when stateful guidance inspects raw input. Tests
set previous_response_id, invoke the real injector and assert raw/parsed/reparsed
arrays. Ordinary tool outputs with call_id remain protocol items because the
shared helper rejects them. Invalid/partial/opaque envelopes retain their current
classification; the complete validator is already covered by parent regressions.

No persisted schema, configuration or role changes. Existing input shape -> shared
validation -> raw insertion index -> stored raw input -> later parser is the full
data flow. The helper remains pure and adds no optional subsystem dependency.
Review uses the actual diff; all runtime checks execute in GitHub Actions.

## A audit amendment
Use the parse-time previous_response_id pattern from multi-agent-compat.test.ts:1075-1089 for envelope-alone and leading-result cases. The raw body must contain that field before parseRequest and retain it during reparse; do not copy the post-hoc parsed.previousResponseId assignment fixture at1029. For historical-prefix coverage use the1043-1072 pattern with explicit _replayPrefixLen and _continuationConversationMessageIndex, and put an old external envelope inside that preserved prefix. Assert parsed boundary before injection as well as raw/parsed/reparsed ordering. This closes the auditor's false-green fixture concern.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Implementation and verification boundary

The raw conversational-item predicate now reuses externalTaskInputContent, matching
the parsed continuation predicate without another envelope validator. Replay-prefix
skipping and the existing fallback remain unchanged.

Three new cases parse with previous_response_id already in the raw body, exercise
external input alone or after a real protocol result, preserve a historical external
envelope in the replay prefix, and compare raw/parsed/reparsed role-content order.
They retain the stateful field during reparse and assert the initial parsed boundary,
avoiding a fixture that could accidentally validate stateless behavior.

Apply this review fix to #3743. Source review and exact-head hosted CI are recorded
on that PR and in the cycle receipt; no local test suite or live Kiro request is run.
After verification, resolve the review and refresh the preserved Kiro branch before
its implementation cycle continues.
14 changes: 14 additions & 0 deletions docs-site/src/content/docs/guides/sub-agent-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ Start with **base**. Choose **v1** when cross-provider delegation must work pred
only when you specifically want its newer session model across every catalog entry.
:::

## External task input

Codex can deliver a task's initial input or follow-up in a result-shaped envelope
without a `call_id`. On translated routes, OpenCodex recognizes only the complete
`function_call_output` shape with nonblank `id`, `name` and `namespace` and supported
text/image output, then treats it as a user turn. This also starts the new conversation
boundary during continuation and clears pending reasoning from the preceding turn.
Generated developer guidance is placed before the current task in both parsed
messages and saved raw history, preserving the same order when that history is replayed.

Malformed, empty, opaque or incomplete envelopes still fail validation. Actual tool
results keep their required `call_id`; native passthrough and compaction retain their
existing raw-input handling. See [the adapter contract](/reference/adapters/#external-task-input-on-translated-responses-routes).

## How it works

The selected mode controls the `multi_agent_version` field in every catalog entry Codex reads:
Expand Down
13 changes: 13 additions & 0 deletions docs-site/src/content/docs/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ adapter own retries/timeouts, while `runTurn` supports transports that cannot be
HTTP fetch followed by one response stream. [`bridge.ts`](/reference/architecture/#the-bridge)
then turns the events into Responses SSE.

## External task input on translated Responses routes

Codex task coordination can deliver input as `function_call_output` with nonblank
`id`, `name` and `namespace` fields and no `call_id` property. OpenCodex maps this
complete envelope to a user message before adapter translation. Its output must be
nonblank text or a fully supported array of text and `input_image` URL parts. Text
and image order are preserved; image detail `original` maps to `high`.

Empty content, malformed or opaque parts, file-id-only images and partial envelopes
remain invalid. Ordinary function/custom tool results still require a nonempty
`call_id`. The envelope metadata identifies a compatibility shape and grants no
additional permissions. Native passthrough and compaction retain their raw-body rules.

## `openai-chat`

**Targets:** OpenAI **Chat Completions** (`POST {baseUrl}/chat/completions`; a trailing `/chat/completions` or `/` on `baseUrl` is stripped first) and every compatible
Expand Down
8 changes: 8 additions & 0 deletions src/responses/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { toolSearchDescription, toolSearchParameters } from "./tool-search-compa
import { isObj, inputContentParts, outputTextOf, outputToToolResultContent, toolOutputContainsEncryptedContent } from "./parser-content";
import { mapToolChoice, buildTools, customToolNamespaces } from "./parser-tools";
import { parseTextFormat } from "./parser-text-format";
import { externalTaskInputContent } from "./task-input";

/**
* Wrap a remembered proxy-side signature as provider metadata for a replayed tool call.
Expand Down Expand Up @@ -146,6 +147,7 @@ export function parseRequest(
const item = data.input[inputIndex];
const effectiveType = (item as { type?: string }).type ?? ("role" in item ? "message" : undefined);
const itemRole = (item as { role?: string }).role;
const externalTaskInput = effectiveType === "function_call_output" ? externalTaskInputContent(item) : undefined;
// Raw protocol items do not map one-to-one onto context messages. Capture the boundary while
// both representations are available so later metadata can stay before conversation in both.
if (
Expand All @@ -154,6 +156,7 @@ export function parseRequest(
&& continuationConversationMessageIndex === undefined
&& (
effectiveType === "agent_message"
|| externalTaskInput !== undefined
|| (effectiveType === "message" && (itemRole === "user" || itemRole === "assistant"))
Comment thread
lidge-jun marked this conversation as resolved.
)
) {
Expand Down Expand Up @@ -429,6 +432,11 @@ export function parseRequest(
}

if (effectiveType === "function_call_output") {
if (externalTaskInput !== undefined) {
pendingReasoning.length = 0;
messages.push({ role: "user", content: externalTaskInput, timestamp: now });
continue;
}
const output = item as { call_id: string; output?: string | unknown[] };
attachPendingReasoningToCallOwner(messages, output.call_id, pendingReasoning);
pendingReasoning.length = 0;
Expand Down
Loading
Loading