Skip to content

feat(telegram): non-streaming tool+commentary progress accumulator#89890

Closed
anagnorisis2peripeteia wants to merge 12 commits into
openclaw:mainfrom
anagnorisis2peripeteia:feat/non-streaming-tool-progress-accumulator
Closed

feat(telegram): non-streaming tool+commentary progress accumulator#89890
anagnorisis2peripeteia wants to merge 12 commits into
openclaw:mainfrom
anagnorisis2peripeteia:feat/non-streaming-tool-progress-accumulator

Conversation

@anagnorisis2peripeteia

@anagnorisis2peripeteia anagnorisis2peripeteia commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

The non-streaming counterpart to persistProgress: when streaming.mode is off, nothing streams during a turn, so tool and commentary progress were lost entirely. This accumulates the commentary + tool-progress lines silently as the turn runs and delivers them as their own message — commentary first, then the tool lines — immediately before the final answer. The user gets the whole progress lane once, when the answer is ready.

What it does (with streaming.mode: "off" + streaming.progress.persistProgress: true):

  • Accumulate silently — on onItemEvent (preamble/commentary) and onToolStart, append to a per-turn log gated on persistProgress. Nothing is sent mid-turn (stream is off).
  • Deliver as a set before the answeremitPersistedProgressSet flushes the accumulated lane as its own durable message ahead of the final answer, but only when there is no live draft stream (so the stream-on persist path in feat(streaming): opt-in persistProgress mode for tool+commentary lines #89850 doesn't double-emit).
  • Run callbacks with the stream off — progress callbacks are allowed to fire when source delivery is suppressed and persistProgress is set, so the accumulator actually runs in mode:off.
  • Guard-free opt-in resolvers — adds resolveChannelStreamingProgressCommentaryEnabled and resolveChannelStreamingProgressToolProgressEnabled, which read the commentary / toolProgress opt-ins without the progress-mode guard (the public guarded resolvers only read them in progress mode). The stream-off accumulator gates each lane on its toggle, so commentary:false or toolProgress:false omits that lane from the accumulated set; the public guarded resolvers are unchanged (live-draft behavior preserved).

Tests

  • tsgo clean
  • channel-streaming.test.ts — added a regression for the toolProgress gate (toolProgress:false omits the tool lane; guard-free and mode-independent); 29/29 pass
  • bot-message-dispatch.test.ts — 103/104 pass; the one failure (records streamed final replies into the prompt context cache) is a pre-existing Windows /tmp store-roundtrip issue that reproduces on the unmodified base commit (it is green on Linux CI).

Proof

Captured via a local recreation of the Mantis (telegram-desktop-proof) flow on crabbox, with streaming.mode: "off" + persistProgress: true (the separate streamkey). Bot DM, claude-cli backend, opus-4.8 / max thinking.

Nothing streams during the turn; at final-send the whole lane lands as one set, commentary first, then the tool reads, with the answer as a separate message below it:

stream-off: commentary+tool lane accumulated as its own set, then the answer below

Observed:

💭 I'll read each file and summarize it.
📖 Read … README.md
📖 Read … package.json
📖 Read … tsconfig.json

(separate message) Here's a one-line summary of each: …

Motion (silent turn → the set + answer arrive together at the end): recording

Gateway trace confirms the lane accumulated and emitted with no live stream: emitSet persist=true hasStream=false logLen=4 streamMode=off (1 commentary line + 3 tool lines).

Stacking

Built on #89850 (streaming.progress.persistProgress), which is built on #89834 (claude-cli commentary producer) and #90883 (commentary delivery + render). This branch is rebased on the current #89850 head, so its own diff is just the stream-off accumulator (bot-message-dispatch.ts) + the guard-free resolver (streaming.ts). Merge #90883#89834#89850 first.

Issues

Part of the #87326 progress UX work.

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 3, 2026
@clawsweeper

clawsweeper Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 6, 2026, 2:59 PM ET / 18:59 UTC.

Summary
The PR adds an opt-in Telegram stream-off progress accumulator, shared streaming.progress.persistProgress config/resolvers, CLI commentary bridging, docs, generated config metadata, and focused regression tests.

PR surface: Source +321, Tests +591, Docs +1, Generated 0. Total +913 across 23 files.

Reproducibility: not applicable. as a feature PR. Source inspection confirms current main lacks this off-mode persistence path, and the supplied Telegram proof shows the proposed after-fix behavior.

Review metrics: 2 noteworthy metrics.

  • Shared Config Surface: 1 added shared streaming.progress option. A new channel progress key affects config/schema/docs expectations across channels even though this implementation is Telegram-first.
  • Open Stack Dependencies: 3 prerequisite PRs named. The final reviewed diff and Telegram delivery behavior depend on the prerequisite stack landing in order.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Refresh the branch after the prerequisite stack lands so maintainers can review the final isolated diff.
  • Get maintainer acceptance on whether persistProgress should be shared channel progress config or Telegram-scoped for now.

Mantis proof suggestion
A maintainer-run Telegram Desktop capture would independently verify the visible stream-off ordering and media isolation on the final stacked branch. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

telegram desktop proof: verify streaming.mode off with persistProgress sends one text-only progress message before the final answer and does not copy answer media.

Risk before merge

Maintainer options:

  1. Accept The Shared Progress Contract (recommended)
    Maintainers can accept streaming.progress.persistProgress as a shared channel progress option, then require this branch to be refreshed on the landed prerequisites before merge.
  2. Narrow The Config Surface
    If maintainers do not want a shared channel contract yet, narrow the option and docs to Telegram-owned behavior before merge and leave a later SDK/API decision for other channels.
  3. Pause Until The Stack Lands
    Maintainers can pause review until the three prerequisite PRs land so this branch shows only the stream-off accumulator delta.

Next step before merge

  • [P2] Human review is needed for the shared config/API contract, Telegram delivery-order semantics, and stacked merge order; there is no narrow automated repair to queue.

Security
Cleared: No concrete security or supply-chain concern was found; the diff does not add dependencies, workflows, lockfiles, secret handling, release scripts, or downloaded-code execution paths.

Review details

Best possible solution:

Land or update the prerequisite stack first, then refresh this branch and either explicitly accept the shared persistProgress channel contract or narrow it before merging.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a feature PR. Source inspection confirms current main lacks this off-mode persistence path, and the supplied Telegram proof shows the proposed after-fix behavior.

Is this the best way to solve the issue?

Unclear until maintainer API review. The stream-off accumulator is a plausible narrow Telegram implementation, but the shared persistProgress contract and stacked merge order require maintainer acceptance.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against f2677d55ecf0.

Label changes

Label justifications:

  • P2: This is a normal-priority opt-in Telegram progress UX improvement with meaningful but bounded blast radius.
  • merge-risk: 🚨 compatibility: The PR adds a shared config/API surface and generated channel metadata that maintainers must accept as a compatibility contract.
  • merge-risk: 🚨 message-delivery: The PR changes Telegram delivery ordering by adding an opt-in durable progress message before final replies in stream-off turns.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (recording): The PR body and inspected Telegram Desktop images show after-fix real behavior for the central stream-off path and media isolation; the linked recording supports the silent-turn-to-final delivery flow.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body and inspected Telegram Desktop images show after-fix real behavior for the central stream-off path and media isolation; the linked recording supports the silent-turn-to-final delivery flow.
  • proof: 🎥 video: Contributor real behavior proof includes video or recording evidence. The PR body and inspected Telegram Desktop images show after-fix real behavior for the central stream-off path and media isolation; the linked recording supports the silent-turn-to-final delivery flow.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. This PR changes visible Telegram chat ordering and message content, so Telegram Desktop proof is useful and already supplied.
Evidence reviewed

PR surface:

Source +321, Tests +591, Docs +1, Generated 0. Total +913 across 23 files.

View PR surface stats
Area Files Added Removed Net
Source 12 329 8 +321
Tests 8 591 0 +591
Docs 2 3 2 +1
Config 0 0 0 0
Generated 1 17 17 0
Other 0 0 0 0
Total 23 940 27 +913

Acceptance criteria:

What I checked:

  • Repository policy applied: Read the full root AGENTS.md, scoped guides for extensions/, src/agents, src/channels, src/plugin-sdk, and docs/, plus Telegram maintainer notes; the review applied the Telegram real-proof requirement and compatibility-sensitive config/API guidance. (AGENTS.md:1)
  • Current main lacks the requested capability: Current main has no persistProgress, guard-free progress resolver, stream-off accumulator, or lossless spill symbols, so this PR is not superseded by main. (f2677d55ecf0)
  • Current Telegram dispatch behavior: On current main, Telegram progress is tied to an active answer draft via resolveChannelStreamingPreviewToolProgress; there is no stream-off durable progress flush path. (extensions/telegram/src/bot-message-dispatch.ts:950, f2677d55ecf0)
  • PR stream-off accumulator: The PR diff adds persistProgressEnabled, streamOffPersistEnabled, a per-turn persistedProgressLog, and a common final-delivery flush that sends accumulated progress as a text-only durable message before final content. (extensions/telegram/src/bot-message-dispatch.ts:908, 86b71f2f3c80)
  • Shared config/API surface: The PR adds persistProgress to the shared channel progress config type and schema, which is compatibility-sensitive because it becomes a channel-agnostic contract while Telegram is the first full implementation. (src/config/types.base.ts:70, 86b71f2f3c80)
  • Sibling channel check: Discord uses createChannelProgressDraftCompositor, while Slack, Matrix, and MS Teams use formatChannelProgressDraftText directly, so the shared resolver/config change deserves maintainer API review across channel progress surfaces. (extensions/discord/src/monitor/message-handler.draft-preview.ts:86, f2677d55ecf0)

Likely related people:

  • vincentkoc: Current-main blame in this shallow checkout points the central Telegram streaming, channel streaming resolver, and CLI parser regions to Vincent Koc's latest main snapshot; use as a routing hint, not blame. (role: recent area contributor; confidence: medium; commits: 7a62cd5efc4d; files: extensions/telegram/src/bot-message-dispatch.ts, src/channels/streaming.ts, src/agents/cli-output.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 3, 2026
@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

Inter-tool commentary (assistant text emitted before a tool call, surfaced by
the CLI parser as a stream:"item", kind:"preamble" agent event) landed on the
agent-event bus with no subscriber and was silently dropped: runCliAgentWithLifecycle
bridges the assistant, reasoning, and tool streams to channel callbacks, but the
item/preamble stream had no bridge. Add createCommentaryEventBridge to forward it
to onItemEvent, so CLI commentary reaches the channel's commentary render hook.

This is the CLI-dispatch delivery half of the commentary feature: the parser
emission (claude-cli) feeds the bus; this bridge delivers it to the channel.
Addresses the claude-cli case of intermediate-text-lost (openclaw#87326 / openclaw#84486).
Commentary lines carry noCompact so the progress-draft renderer does not compact
them like tool lines — assistant prose renders in full, spilling to a new message
at the channel limit rather than truncating mid-sentence.
Detect text accumulated before tool_use blocks in the Claude CLI
streaming parser and emit it as commentary via a new onCommentaryText
callback. This enables the same commentary progress display that the
Codex backend already provides through preamble item events.

- Add onCommentaryText optional callback to createCliJsonlStreamingParser
- Flush accumulated assistantText as commentary when content_block_start
  with tool_use type is encountered
- Track last flushed position to avoid duplicate emissions on consecutive
  tool_use blocks without intervening text
- Wire the callback in both execute.ts (regular CLI spawn + live session)
  and claude-live-session.ts, emitting AgentItemEventData with
  kind=preamble and progressText
- Add 3 test cases covering: text before tool_use, empty text before
  tool_use, and consecutive tool_use dedup
anagnorisis2peripeteia added a commit to anagnorisis2peripeteia/openclaw that referenced this pull request Jun 6, 2026
@anagnorisis2peripeteia anagnorisis2peripeteia force-pushed the feat/non-streaming-tool-progress-accumulator branch from 8692ed4 to 4034483 Compare June 6, 2026 13:07
@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

Updated: stream-off accumulation now works — fixed the double-wrap (tool lines) and the progress-mode guard (commentary), added a guard-free resolveChannelStreamingProgressCommentaryEnabled for the off path, and rebased onto the amended #89850. Proof (commentary-first set delivered before the answer with the stream off) is in the body. @clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation agents Agent runtime and tooling labels Jun 6, 2026
@anagnorisis2peripeteia anagnorisis2peripeteia force-pushed the feat/non-streaming-tool-progress-accumulator branch from aed9df8 to 62fa8b4 Compare June 6, 2026 15:52
@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 6, 2026
@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 6, 2026
anagnorisis2peripeteia added a commit to anagnorisis2peripeteia/openclaw that referenced this pull request Jun 6, 2026
@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

Added real-behavior proof for the media-final path (Telegram Desktop, stream-off + persistProgress):

stream-off progress set (text-only) ahead of a media/photo final answer

The accumulated progress set is delivered first as a single text-only message (💭 commentary interleaved with 📖 tool reads); the answer's photo (sendPhoto) is a separate message — the progress set does not carry the answer's media. This confirms the text-only-payload fix isolates the progress set from answer-content fields, with a media final.

Note on strict media-only (no answer text): a safety-conscious model won't reliably emit text-free media on command (it treated "reply with only this" as link-laundering and refused), so the no-text branch of the common-path flush is covered deterministically by the dispatch regression flushes the stream-off persisted progress set even for a media-only final answer (exercises the info.kind === "final" flush with a media-only payload and no answer-text segment).

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 6, 2026
Moving the progress-set flush to the common final path made it fire on the
bare absence of an answer-lane draft (!answerLane.stream), which is also true
in partial/progress turns that lack a live draft (e.g. selected-quote replies)
— so those turns could get a surprise durable progress message.

Gate accumulation, suppressed-source callback forwarding, and emission on a
single streamOffPersistEnabled = persistProgressEnabled && streamMode === 'off'
so the accumulator only ever engages in genuine stream-off turns. Regression:
a no-draft partial-mode turn with persistProgress emits no progress set.
@anagnorisis2peripeteia

Copy link
Copy Markdown
Contributor Author

Fixed the [P2/P1] no-draft leak (bot-message-dispatch.ts:1772): the stream-off accumulator was gated on the bare absence of a draft (!answerLane.stream), which is also true in partial/progress turns that lack a live draft (e.g. selected-quote replies). It's now gated on a single streamOffPersistEnabled = persistProgressEnabled && streamMode === "off" across accumulation, suppressed-source callback forwarding, and emission — so it only ever engages in genuine stream-off turns. Regression added: a no-draft partial-mode turn with persistProgress emits no progress set.

On the strict media-only proof: a safety-conscious model won't emit text-free media on command (asked to "reply with only this," it refused as link-laundering), so a true no-caption media-only final isn't naturally elicitable in the proof harness. The closest real capture is the media-with-caption one above (which proves the progress set stays text-only and the photo lands only on the answer — the payload-isolation fix), and the strict no-text branch of the common-path flush is covered deterministically by the dispatch regression flushes the stream-off persisted progress set even for a media-only final answer. Per the review's own note, the remaining proof/stack/config items need human maintainer review.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 6, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label Jun 6, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 6, 2026
@anagnorisis2peripeteia anagnorisis2peripeteia marked this pull request as ready for review June 6, 2026 18:51
@obviyus

obviyus commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Closing as superseded by #91976, which just landed — with /verbose on, commentary and CLI tool summaries now arrive as durable standalone messages in streaming.mode: "off" as well, covering this PR's use case without the persistProgress config surface. Thanks @anagnorisis2peripeteia!

@obviyus obviyus closed this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: telegram Channel integration: telegram docs Improvements or additions to documentation mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants