Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ observed upstream server failures as compatibility behavior. See

Report validation failures with official evidence through the typed field error,
which emits `invalid_request_error` with the observed param and message; keep
other local codes until their official fields are sampled. Agent configuration
other local codes until their official fields are sampled. Every 409 has type
`conflict_error`. Session input conflicts and changed tool results also use code
`conflict_error`; documented Core-only conflicts, such as Idempotency-Key reuse,
sandbox administration and Environment input states, keep their local codes. Agent configuration
(saved create/update and the inline Session agent) uses one path-tracking
validator of the pinned shapes before its parsers and harness admission, which
keep their local codes; do not grow it into a JSON Schema engine. A malformed path
Expand Down Expand Up @@ -2295,7 +2298,9 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti
Turn/call identity selects an existing call; admission never creates a Turn for
a result. Save the complete result and its input retry record in the same Session
transaction. Any invalid target, conflicting result or later batch error rolls
back the whole request. Identical saved results remain retryable after termination
back the whole request. Resolve targets only after the tenant Session lookup:
an unknown call or a call of another Turn is 400 `invalid_request_error`, and
missing or foreign Sessions keep one 404. Identical saved results remain retryable after termination
without applying them again. The execution input cursor skips function results;
their separate native receipts still determine application. Public result events
validate variant-specific fields and required values before admission; retain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,10 @@ describe("Session deletion", () => {
await requestSessionCancelBeforeDelete(core, "session-1", "cancel-key");
expect(cancelTurn).toHaveBeenCalledWith("session-1", "cancel-key");

// Current Cores report a rejected cancellation with the official conflict
// fields; earlier Cores used turn_conflict. Neither is a deletion conflict.
for (const [error, kind, text] of [
[new AgentCoreError("Earlier input to this Session is still pending.", 409, "conflict_error", null, "conflict_error"), "request_failed", "rejected the cancellation (409): Earlier input to this Session is still pending."],
[new AgentCoreError("pending input", 409, "turn_conflict"), "request_failed", "rejected the cancellation (409)"],
[new TypeError("connection lost"), "request_failed", "result is unknown"],
[new AgentCoreError("missing", 404), "not_found", "not found"],
Expand All @@ -362,7 +365,7 @@ describe("Session deletion", () => {
expect(failure).toMatchObject({ kind });
expect((failure as Error).message).toContain(text);
}
expect(cancelTurn).toHaveBeenCalledTimes(4);
expect(cancelTurn).toHaveBeenCalledTimes(5);
expect(deleteSession).not.toHaveBeenCalled();
});

Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/execution-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ and Environment Plugin MCP have separate inventories and qualification.
| Prepared and active messages, `sessions.events.create` | Ordered `input_text`/`input_image` arrays retain original content and distinct public user Items. HTTP 202 confirms persistence; native receipts establish application. Initial/prepared/active Docker PNG/JPEG: M2; active PNG on `none`: M1. [Shared admission](../../services/agents-api/internal/api/inputs.go). | Codex flattens native messages with blank-line separators. Claude can fold or queue native turns; it does not promise Codex's same-native-turn behavior. Public durability does not prove native consumption. Claude SDK and MiniMax Code reject messages without an image or non-whitespace text at admission (`unsupported_or_invalid_configuration`); Codex delivers them unchanged. |
| Structured output, `agent.text.format` | Save/inherit/freeze `{type:json_schema,schema:...}`. Claude SDK object-root, single Agent, medium verbosity, ordinary functions returning text: S1 (`none`) and S2 (Docker, including prepared/active input and Files/Artifacts). Native final text is retained unchanged. [Contract](structured-output.md), [profile](../../services/agents-api/internal/engine/claude.go). | An explicit non-object root type is a protocol error for every harness ([validation](official-semantics-alignment.md#agent-configuration-validation--september-23)). Codex/MiniMax, schemas without an object root, schema numbers changed by binary64, self-hosted, Skills/Plugins, MCP, Subagent and discovery combinations reject execution. No output repair, coercion or extra model loop. Arbitrary schema dialects are unverified. |
| Function configuration, saved/inline Agents | Required name/description/schema; `defer_loading` defaults false. Protocol errors, repeated names and explicit non-object root types reject with the official fields ([validation](official-semantics-alignment.md#agent-configuration-validation--september-23)). Saved references resolve into an immutable Session snapshot. Codex/Claude real calls: F1/F2/M2/S1/S2. [Parser](../../services/agents-api/internal/api/function_configuration.go), [saved tools](../../services/agents-api/internal/api/saved_tools.go). | MiniMax public functions reject. Claude requires an explicit object root. Local nonblank/512-byte name and 64-definition Session bounds are compatibility gaps. Saving configuration alone does not qualify execution. |
| Function-result admission, `events.create` | Required `turn_id`, `call_id`, `success`; optional nullable `error` and `output`. Output is string or ordered text/image content. Scoped atomic batches retain field presence, original content and retry identity. Same result retries are accepted; changed results conflict, including after terminal state. F1/F2/M2 plus [controlled SDK/raw checks](../../services/agents-api/tests/official_function_inputs.py). [Parser](../../services/agents-api/internal/api/function_inputs.go), [Store](../../services/agents-api/internal/store/function_results.go). | Admission is separate from application and public Item publication. Invalid or unqualified content cannot consume a pending call. Exact hosted errors, defaults and publication timing remain unverified. |
| Function-result admission, `events.create` | Required `turn_id`, `call_id`, `success`; optional nullable `error` and `output`. Output is string or ordered text/image content. Scoped atomic batches retain field presence, original content and retry identity. Same result retries are accepted; changed results and results after cancellation return 409 `conflict_error`, including after terminal state. In the caller's Session, an unknown call or a call of another Turn returns 400 `invalid_request_error` without changing the pending action; missing and foreign Sessions stay 404 ([error rows](official-semantics-alignment.md#session-input-conflicts-and-result-targets--september-23)). F1/F2/M2 plus [controlled SDK/raw checks](../../services/agents-api/tests/official_function_inputs.py). [Parser](../../services/agents-api/internal/api/function_inputs.go), [Store](../../services/agents-api/internal/store/function_inputs.go). | Admission is separate from application and public Item publication. Invalid or unqualified content cannot consume a pending call. Core messages omit the call and executor IDs that official messages name; hosted defaults and publication timing remain unverified. |
| Function text results and native application | Codex waits for a matching live root dynamic-tool completion; Claude waits for a matching live root native tool result. Text/error results, retry/conflict, cancellation and cold continuation: F1/F2. [Receipt contract](function-result-images.md). | Transport writes alone do not confirm application. Confirmation is not provider consumption, crash recovery or exactly-once external effects. No automatic result replay. |
| Function image results | Successful ordered inline PNG/JPEG with text, large PNG and image-only JPEG: Codex/Claude `none` F1/F2; Docker M2. Public Items retain submitted bytes. Codex receipt regression: F2. | Claude rejects failed images and remote references before persistence; native resizing may change its image bytes. MiniMax functions and self-hosted image workflows are unqualified. Other Codex image/error/reference combinations cannot be inferred from successful-inline evidence. |
| Deferred function discovery | Type-only `tool_search` plus mixed eager/deferred functions: Claude SDK 0.3.269/native 2.1.269, Kimi K3, single Agent, medium, `none`, text results; text and PNG input D1. Native provider observations establish lazy schema loading. [Contract](tool-search.md). | A repeated `tool_search` is a protocol error. Codex/MiniMax discovery, search-only/missing-search, workspace, MCP, structured-output and Subagent combinations remain gaps. Opaque native policy changes lack a reliable pre-input deferral signal. Saved tools include `tool_search`; the pinned Session response union excludes it. Exact hosted projection is unverified. |
Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/history-events-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ Deferred, with evidence retained in `findings.json`:
them under native history ownership.
- EVT-09 and EVT-10: null-valued `output_index`, `phase` and `error` fields and
the initial assistant content belong to a separate serialization batch.
- EVT-11 and EVT-12: unknown call/Turn result and conflict error codes belong to
ERROR-PROTOCOL-001.
- EVT-11 and EVT-12: unknown call/Turn result and conflict error codes were
since aligned by the [input conflict batch](official-semantics-alignment.md#session-input-conflicts-and-result-targets--september-23).
- EVT-13: official Session usage became null when any root Turn usage was
unknown; Core sums the known Turns. The in-progress case is unverified.
- EVT-19: the Core terminal sequence for a Turn cancelled mid-text is recorded by
Expand Down
77 changes: 77 additions & 0 deletions contracts/agents-api/official-semantics-alignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,80 @@ test creates Sessions and submits events over HTTP, reads back the exact user
Item text, and proves the W4 rejections write nothing; the pinned-SDK initial
input script asserts the same. Real Core, daemon and model acceptance is recorded
separately by the coordinator.

## Session input conflicts and result targets — September 23

This batch gives every 409 the official conflict type and aligns the conflict and
tool result target errors of `events.create`, from Core main `0035435a`. Evidence
comes from the campaign scans recorded privately in
`~/.parsar/remediation/20260923/campaign-scan-4/errors/findings.json` (ERR-22 and
ERR-27, raw records in `official/results.json`: `sessB-message-while-running`,
`sessA-delete-while-waiting`) and
`~/.parsar/remediation/20260923/campaign-scan-2/events-tools/findings.json`
(EVT-11, EVT-12 and EVT-14, raw records in `official/calls-s2.json`:
`s2-result-unknown-call`, `s2-result-unknown-turn`,
`s2-result-duplicate-after-terminal`, `s2-result-changed-after-terminal` and
`s2-result-after-cancel`). Every observed official 409 has type and code
`conflict_error` and a null param.

| Row | Case | Core behavior |
| --- | --- | --- |
| CF1 | Every 409 response (ERR-27) | Type `conflict_error`. The code stays specific to the case (CF2–CF5). |
| CF2 | `events.create` input that the Session cannot accept in its current state: a tool result after its Turn was cancelled, or ended without a saved result (EVT-12), and any other Turn conflict on this route; a batch while earlier input still waits for admission, such as the reserved initial input of a provisioning hosted Session or of a self-hosted Session awaiting its connection (ERR-22) | 409 with code `conflict_error` and a null param. Core keeps its message "The Turn cannot accept this input in its current state."; pending input reports "Earlier input to this Session is still pending." (official: "session initial input is still pending"). |
| CF3 | A tool result that differs from the call's saved result, before or after its Turn ends (EVT-12) | 409 `conflict_error`, "The tool call already has a different result." |
| CF4 | Idempotency-Key reuse with a different body on Session creation or `events.create` | Unchanged local code `idempotency_conflict` and message, with type `conflict_error`. Request idempotency is a documented Core extension of these operations. |
| CF5 | Other Core-only conflicts: `sandbox_deployment_conflict`, `runtime_node_in_use`, `runtime_local_node_configured`, `environment_unavailable`, `environment_input_expired`, `environment_input_cancelled`, `runtime_history_unsupported`, and `turn_conflict` from an Environment file write while Session work or input is active | Codes and messages unchanged, with type `conflict_error`. |
| CF6 | A tool result whose `call_id` names no function call of the caller's own Session, with any `turn_id` (EVT-11) | 400 with type and code `invalid_request_error`, param null, "Unknown pending tool call." Nothing is written and the pending action is unchanged. |
| CF7 | A tool result for a call of the Session whose `turn_id` names another Turn, an unknown UUID or no UUID at all (EVT-11) | 400 `invalid_request_error`, param null, "The tool call belongs to a different Turn." Nothing is written. |
| CF8 | Any input to a missing, malformed or foreign Session | Unchanged: the byte-identical 404 `not_found_error`, whatever the result target. |
| CF9 | An identical tool result repeated before or after its Turn ends (EVT-14) | Unchanged: 202 without another application or event. The official repeated `item.added` is not copied. |

Decisions:

- The error writer selects type `conflict_error` from the 409 status, so later
conflicts cannot drift. The Session input writer maps Turn conflicts to code
`conflict_error`; other routes keep `turn_conflict`, because official conflicts
there, such as an Environment file write during work, are unsampled.
- Result targets are resolved under the tenant Session lock after the Session
lookup. A well-formed Turn ID is looked up in that Session and must own the call;
otherwise the Session's own calls decide between CF6 and CF7. The `turn_id` is
therefore no longer rejected as a malformed UUID before the Session lookup, and
malformed, missing and foreign Sessions keep one 404. The decision reads only
the caller's Session, so it reveals nothing about other Sessions or tenants.
- The official messages name the call or internal executor IDs ("Unknown pending
tool call: <call_id>", "function call exec-... belongs to a different managed
agent turn"). Core's messages are fixed and repeat neither caller input nor
internal identifiers.
- Checks keep their order: request validation, the Session lookup, the retry
lookup (CF4), for batches with a message the Environment file-write gate (a
Turn conflict, also CF2 with the Turn message), the pending input gate (CF2),
then each event in batch order. A
batch sent while input is pending therefore returns the CF2 409 even when its
result target is unknown; the official order between these errors is
unobserved. An empty `turn_id` or a
blank `call_id` remains the generic 400 `invalid_request`.
- The official pending-input sample is the asynchronous admission window of
`none` initial input (ERR-22). Core admits `none` input synchronously and does
not emulate that window; the same fields apply to Core's reserved hosted and
self-hosted input, initial or later.
- The TypeScript client and Core Web did not branch on the old codes. The client
documents that `isSessionDeletionConflict` classifies only a `deleteSession`
failure, since input conflicts now share its code. Cores before this batch
returned 409 `turn_conflict` or `idempotency_conflict` with type
`invalid_request_error`, and 404 for unknown result targets; clients that span
both should treat any 409 as a conflict, and a 400 on new Cores or a 404 on
older Cores as an unknown result target.

Unchanged: the ERR-22 asynchronous admission window (an architectural difference),
Idempotency-Key semantics, Session-level 404 isolation, admission timing and the
schema. The pinned SDK still retries a 409 by default; the status did not change.

Go API tests pin the exact CF2–CF9 bodies and the conflict type of every Core-only
409 code. A real-PostgreSQL HTTP test replays CF2–CF4 and CF6–CF9 with tenant B
requests, missing and malformed Sessions, a rolled-back mixed batch, a
whole-database digest and Session reads proving that every rejection writes
nothing and keeps the pending action. Store tests cover target classification and
rollback. The pinned-SDK scripts `official_function_inputs.py`,
`official_pending_actions_native.py` and `official_session_creators.py` assert the
new fields, and TypeScript client and Core Web unit tests cover them. Real Core,
daemon and model acceptance is recorded separately by the coordinator.
35 changes: 21 additions & 14 deletions contracts/agents-api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4550,20 +4550,27 @@ paths:
batches reuse existing scoped result admission and application receipts without
creating a Turn or bypassing a pending reservation. Mixed prepared-environment
batches remain unsupported. HTTP expiry/cancellation use local 409 environment_input_expired/environment_input_cancelled
errors; exact hosted failure mapping is unverified. Losing execution ownership
returns 503. The response write deadline accommodates the admission window
for either prepared Environment, independently of new-hosted-admission and
executor URL settings. Disconnecting the waiting HTTP request does not cancel
retained work or restart its deadline. Retry keys identify the whole ordered
batch. Function output accepts text or ordered text/image parts subject to
engine support; Claude SDK accepts text results and, on none and qualified
openai_hosted, successful inline PNG/JPEG results, preserving ordered content;
error images and remote references reject before admission. Native image resizing
may change bytes. Runtime image-result support is checked only for image-bearing
delivery. Codex and Claude SDK on none and qualified openai_hosted accept
ordered inline PNG/JPEG image messages. Self-hosted profiles and other engines
remain text-only; remote image URLs are unsupported. Image references are
retained unchanged without service-side downloads.
errors; exact hosted failure mapping is unverified. Input the Session cannot
accept in its current state, such as a result after cancellation or a batch
while earlier input is pending, and a result that differs from the call's
saved result return 409 with type and code conflict_error; reusing an Idempotency-Key
with a different batch returns the local 409 idempotency_conflict. Inside
an owned Session, a result for an unknown call or for a call of another Turn
returns 400 invalid_request_error and changes nothing; missing and foreign
Sessions return 404. Losing execution ownership returns 503. The response
write deadline accommodates the admission window for either prepared Environment,
independently of new-hosted-admission and executor URL settings. Disconnecting
the waiting HTTP request does not cancel retained work or restart its deadline.
Retry keys identify the whole ordered batch. Function output accepts text
or ordered text/image parts subject to engine support; Claude SDK accepts
text results and, on none and qualified openai_hosted, successful inline PNG/JPEG
results, preserving ordered content; error images and remote references reject
before admission. Native image resizing may change bytes. Runtime image-result
support is checked only for image-bearing delivery. Codex and Claude SDK on
none and qualified openai_hosted accept ordered inline PNG/JPEG image messages.
Self-hosted profiles and other engines remain text-only; remote image URLs
are unsupported. Image references are retained unchanged without service-side
downloads.
parameters:
- description: agents=v1
in: header
Expand Down
Loading
Loading