Skip to content

feat(agents): [2/n] reattach tool handlers after disconnect - #4008

Closed
apcha-oai wants to merge 22 commits into
mainfrom
apcha/agents-reattach-handlers
Closed

apcha-oai wants to merge 22 commits into
mainfrom
apcha/agents-reattach-handlers

Conversation

@apcha-oai

@apcha-oai apcha-oai commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Reconnect to a hosted session with the same tool handlers after a worker or event connection stops. Omitting input attaches to existing work without sending another prompt.

Before, the raw event stream leaves pending-call dispatch and final-answer recovery to the application:

with client.beta.agents.sessions.events.stream(saved_session_id) as events:
    for event in events:
        # Dispatch recovered function calls and submit their results.
        # Recover missed final messages from this turn's durable items.
        ...

After:

with client.beta.agents.sessions.stream(
    saved_session_id,
    tool_handlers={"lookup_order": lookup_order},
) as stream:
    result = stream.get_final_result()
print(result.output_text)

Pending calls come from the reattached stream and use the existing dispatcher. Explicit result collection retrieves the selected turn's durable messages. Already-idle sessions drain without inventing a result from an earlier turn. Reattachment uses at-least-once tool-call delivery with application-owned recovery: restart the worker, recover its saved session ID, and reattach handlers. Applications own idempotency for handler mutations. Sync and async APIs support the same flow.

Stack

@apcha-oai
apcha-oai requested a review from a team as a code owner October 1, 2026 01:02
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T16:45:21.893172Z c1212a2 Manual request
🔒 Security Review ✅ Completed 2026-10-01T16:43:18.475867Z c1212a2 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

49 mixed files remain; 1 existing customization changed.

Compared 10f88169b0f3 → c1212a23fbe8. Generated baselines verified.

File Result Current custom patch
src/openai/resources/beta/agents/sessions/sessions.py Existing customization changed +81 / −11
48 existing customizations unchanged
  • api.md
  • scripts/castiron/README.md
  • scripts/castiron/custom_code_report.py
  • scripts/castiron/test_custom_code_report.py
  • src/openai/init.py
  • src/openai/_client.py
  • src/openai/resources/audio/transcriptions.py
  • src/openai/resources/audio/translations.py
  • src/openai/resources/beta/beta.py
  • src/openai/resources/beta/responses/responses.py
  • src/openai/resources/beta/threads/runs/runs.py
  • src/openai/resources/beta/threads/threads.py
  • src/openai/resources/chat/completions/completions.py
  • src/openai/resources/embeddings.py
  • src/openai/resources/files.py
  • src/openai/resources/live/forks.py
  • src/openai/resources/live/live.py
  • src/openai/resources/live/sideband.py
  • src/openai/resources/realtime/api.md
  • src/openai/resources/realtime/realtime.py
  • src/openai/resources/responses/responses.py
  • src/openai/resources/uploads/uploads.py
  • src/openai/resources/vector_stores/file_batches.py
  • src/openai/resources/vector_stores/files.py
  • src/openai/resources/videos.py
  • src/openai/resources/webhooks/init.py
  • src/openai/resources/webhooks/webhooks.py
  • src/openai/types/beta/agent_session_message.py
  • src/openai/types/beta/session_turn_error.py
  • src/openai/types/chat/init.py
  • src/openai/types/chat/chat_completion_message_tool_call.py
  • src/openai/types/fine_tuning/fine_tuning_job_integration.py
  • src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
  • src/openai/types/realtime/realtime_error_event.py
  • src/openai/types/responses/init.py
  • src/openai/types/responses/response.py
  • src/openai/types/responses/response_function_web_search.py
  • src/openai/types/responses/response_function_web_search_param.py
  • src/openai/types/responses/responses_client_event.py
  • src/openai/types/responses/responses_client_event_param.py

8 more in the full report.

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 36893538883 --repo openai/openai-python \
  --name castiron-custom-code-36893538883-1 --dir /tmp/castiron-custom-code-36893538883-1
git apply --stat /tmp/castiron-custom-code-36893538883-1/custom-code.patch
cat /tmp/castiron-custom-code-36893538883-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin 10f88169b0f3929b4e2528aad7a825df31163156 c1212a23fbe84cc987c1cc470a21baa3344389de
python3 scripts/castiron/custom_code_report.py report \
  --base 10f88169b0f3929b4e2528aad7a825df31163156 \
  --head c1212a23fbe84cc987c1cc470a21baa3344389de --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-c1212a23fbe8
cat /tmp/castiron-custom-code-c1212a23fbe8/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@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: 7fcfa98392

ℹ️ 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 thread src/openai/lib/streaming/agents/_streams.py Outdated
Comment thread src/openai/lib/beta/agents/_attachment.py Outdated
Comment thread src/openai/lib/beta/agents/_attachment.py Outdated
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 552711cb07

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@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: 552711cb07

ℹ️ 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 thread src/openai/lib/beta/agents/_attachment.py Outdated
Comment thread src/openai/lib/beta/agents/_attachment.py Outdated
Comment thread src/openai/lib/streaming/agents/_streams.py
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

1 similar comment
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ 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".

@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@apcha-oai
apcha-oai changed the base branch from apcha/agents-typed-results to main October 1, 2026 01:33
@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: bfddcb8747

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@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: bfddcb8747

ℹ️ 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 thread src/openai/lib/streaming/agents/_streams.py
Comment thread src/openai/lib/beta/agents/_attachment.py Outdated
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@openai-sdks

openai-sdks Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

OkTest Summary

✅ 236/236 SDK tests passed in 9.973s for Python SDK PR #4008.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 135ms
tests/chat-completions-create.test.ts ✅ Passed 244ms
tests/chat-completions-stream.test.ts ✅ Passed 247ms
tests/files-content-binary.test.ts ✅ Passed 156ms
tests/files-create-multipart.test.ts ✅ Passed 171ms
tests/files-list-pagination.test.ts ✅ Passed 150ms
tests/initialize-config.test.ts ✅ Passed 106ms
tests/instance-isolation.test.ts ✅ Passed 143ms
tests/models-list.test.ts ✅ Passed 192ms
tests/responses-background-lifecycle.test.ts ✅ Passed 173ms
tests/responses-body-method-errors.test.ts ✅ Passed 293ms
tests/responses-cancel-timeout.test.ts ✅ Passed 175ms
tests/responses-cancel.test.ts ✅ Passed 200ms
tests/responses-compact-retries.test.ts ✅ Passed 274ms
tests/responses-compact.test.ts ✅ Passed 239ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 153ms
tests/responses-create-advanced.test.ts ✅ Passed 171ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.199s
tests/responses-create-errors.test.ts ✅ Passed 159ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 204ms
tests/responses-create-retries.test.ts ✅ Passed 170ms
tests/responses-create-stream-failures.test.ts ✅ Passed 705ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 227ms
tests/responses-create-stream-wire.test.ts ✅ Passed 2.867s
tests/responses-create-stream.test.ts ✅ Passed 240ms
tests/responses-create-terminal-states.test.ts ✅ Passed 219ms
tests/responses-create-timeout.test.ts ✅ Passed 169ms
tests/responses-create.test.ts ✅ Passed 552ms
tests/responses-delete.test.ts ✅ Passed 150ms
tests/responses-input-items-errors.test.ts ✅ Passed 372ms
tests/responses-input-items-list.test.ts ✅ Passed 266ms
tests/responses-input-items-options.test.ts ✅ Passed 257ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 199ms
tests/responses-input-tokens-count.test.ts ✅ Passed 165ms
tests/responses-malformed-inputs.test.ts ✅ Passed 1.992s
tests/responses-not-found-errors.test.ts ✅ Passed 263ms
tests/responses-parse.test.ts ✅ Passed 240ms
tests/responses-retrieve-retries.test.ts ✅ Passed 152ms
tests/responses-retrieve.test.ts ✅ Passed 145ms
tests/responses-stored-method-errors.test.ts ✅ Passed 592ms
tests/retry-behavior.test.ts ✅ Passed 3.061s
tests/sdk-error-shape.test.ts ✅ Passed 334ms

View OkTest run #36893491299

SDK merge (acc9d008c420) · head (c1212a23fbe8) · base (10f88169b0f3) · OkTest (e7cf6535e0ca)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ 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 thread src/openai/lib/beta/agents/_attachment.py Fixed
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: e3376c837d

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: e3376c837d

ℹ️ 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".

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ 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".

@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@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: 9a1d2d8749

ℹ️ 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 thread src/openai/lib/beta/agents/_attachment.py Outdated

@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: de808413ad

ℹ️ 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 thread src/openai/lib/streaming/agents/_streams.py Outdated

@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: 90c79679eb

ℹ️ 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 thread src/openai/lib/beta/agents/_attachment.py Outdated
Comment thread src/openai/lib/beta/agents/_attachment.py
Base automatically changed from apcha/agents-typed-results to main October 1, 2026 16:18
@apcha-oai
apcha-oai force-pushed the apcha/agents-reattach-handlers branch from f5e68d3 to f16973a Compare October 1, 2026 16:26
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex review pls

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: c1212a23fb

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@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: c1212a23fb

ℹ️ 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 +63 to +67
collector.turn is None
or collector.turn.id == self.turn.id
and collector.turn.status not in _TERMINAL
and self.turn.status in _TERMINAL
):

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 Refresh nonterminal turn snapshots in the collector

When attachment initially seeds a queued or in-progress root and a later approval event causes manual diagnostics to refresh that same root to waiting, state.refresh(latest) updates the attachment but this condition refuses to copy the newer snapshot because neither status is terminal. The ensuing requires_action error therefore exposes a stale turn.status (for example, in_progress) alongside a current approval that can only be serviced for the waiting turn. Propagate authoritative same-turn nonterminal refreshes to the collector while retaining the existing protection against downgrading an observed terminal state.

Useful? React with 👍 / 👎.

Comment on lines +128 to +132
def select_candidate(
sessions: Sessions, state: AgentSessionAttachment, candidate: str, options: _RequestOptions
) -> None:
latest = _latest_root(sessions.turns.list(state.session_id, order="desc", **options))
if latest is not None and latest.id == candidate:

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 Request the maximum page size when locating the root

In a long-running multi-agent turn, every subagent turn is newer than its root, so _latest_root() may auto-page through the entire subagent history before finding the current root. This call leaves the turns endpoint at its 20-record default, and attachment invokes the same scan repeatedly during bootstrap, candidate validation, idle refresh, and manual diagnostics; sessions with hundreds of child turns can therefore make dozens of serial requests per lookup and fail reattachment through timeouts or rate limits. Distinct from durable item recovery, these turn-list calls should also request the endpoint's maximum page size in both sync and async paths.

Useful? React with 👍 / 👎.

@apcha-oai

Copy link
Copy Markdown
Contributor Author

Closing this PR for now while we establish a reliable backend recovery contract.

On reattachment, a silent session event stream does not distinguish already-completed work from work the client should continue waiting for. A separate session/history read is not a sufficient boundary either: newly accepted input can still be followed by an idle session snapshot. Returning the latest historical result can therefore select a previous turn, while waiting for completion can wait indefinitely for an event that already happened.

We are avoiding SDK-side history reconciliation and heuristics for this ambiguous recovery case. Reattachment/result recovery is deferred until the backend provides an authoritative attachment state or equivalent execution/completion signal. The file/artifact helpers will proceed separately, without depending on this PR.

@apcha-oai apcha-oai closed this Oct 1, 2026
gh-actions-shared Bot pushed a commit to xf-qubit/openai-python that referenced this pull request Oct 1, 2026
## Summary

Stage selected local files and read an artifact from the exact completed
turn, either in memory or streamed to a local path. The helpers keep
upload IDs available for explicit cleanup and stream downloads to a
caller-chosen path.

Before:

```python
uploaded = client.files.create(file=Path("source.pdf"), purpose="user_data")
inputs = [{"type": "file_id", "file_id": uploaded.id, "path": "/workspace/source.pdf"}]
# Pass inputs when creating the hosted environment, then run the session.
artifact = next(a for a in client.beta.agents.sessions.artifacts.list(result.session_id)
                if a.turn_id == result.turn_id and a.path == "/workspace/outputs/report.md")
with client.beta.agents.sessions.artifacts.with_streaming_response.content(
    artifact.id, session_id=result.session_id,
) as content:
    content.stream_to_file("report.md")
```

After:

```python
prepared = client.beta.agents.environments.files.prepare({
    "/workspace/source.pdf": Path("source.pdf"),
})
# Pass prepared.files when creating the hosted environment, then run the session.
artifacts = client.beta.agents.sessions.artifacts.for_result(result)
report_bytes = artifacts.content("/workspace/outputs/report.md").content
# Or stream to an application-owned path:
artifact = artifacts.download("/workspace/outputs/report.md", to=Path("report.md"))
```

`prepare_directory(..., include=[...])` selects a directory snapshot;
`files.upload(...)` uploads and stages one file in an existing
environment. Sync and async helpers live under the beta Agents
namespace. They preflight selected files before uploading, preserve
partial upload ownership on errors, and detect missing or ambiguous
artifacts across all pages.

Local path selection is intended for static application-owned files and
stable directories; it is not a filesystem sandbox for untrusted paths
or hostile local writers.

This PR now targets `main` directly. Reattachment/result recovery is
deferred: a silent attachment cannot reliably distinguish pending work
from an already-completed turn, so these helpers do not depend on
SDK-side recovery heuristics.

### Stack

- openai#4004 (merged
prerequisite)
- openai#4007 (merged
prerequisite)
- openai#4008 (closed/deferred;
not a dependency)
- openai#4009 👈 this PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant