Skip to content

fix(agent): make spoken narration strictly opt-in#3500

Merged
trunk-io[bot] merged 4 commits into
mainfrom
posthog-code/gate-speak-tool-strict-optin
Jul 16, 2026
Merged

fix(agent): make spoken narration strictly opt-in#3500
trunk-io[bot] merged 4 commits into
mainfrom
posthog-code/gate-speak-tool-strict-optin

Conversation

@andrewm4894

Copy link
Copy Markdown
Member

Problem

The speak (spoken narration) tool was running on headless cloud runs — Slack threads and Signals scouts — where nothing can play audio, quietly costing tokens with no listener.

Raised in #team-max-ai. The gating that was believed to protect this (feature flag + user setting + ElevenLabs key) only governs playback on the desktop client. On the agent side, resolveSpokenNarration fell back to isCloudRun(meta), so every cloud run defaulted narration on when no explicit setting was passed. That loaded the speak tool schema and appended its prompt instructions (which tell the agent to "never end a turn silently"), so the model actively emitted speak calls on runs no one was listening to — independent of the flag, setting, or key.

Changes

Make narration strictly opt-in:

  • resolveSpokenNarration now returns true only when a caller explicitly sets spokenNarration. The cloud/sandbox default-on is gone, so headless runs never load the tool or its instructions and never pay the token cost.
  • The desktop — the only client that can play audio and that knows the flag, setting, and key — computes the boolean and passes it. It now also requires a configured ElevenLabs key alongside the setting on both the start and reconnect paths.
  • Refreshed the now-stale comments describing the old default-on behaviour.

Why: narration should only run where someone can actually hear it and has explicitly turned it on; it should never silently burn tokens on automated runs.

Note: requiring an ElevenLabs key means the macOS say fallback no longer triggers narration on its own — intentional per the requested gating, but flagging in case we want the fallback to count.

How did you test this?

  • pnpm --filter @posthog/agent exec vitest run on session-meta.test.ts (rewritten for opt-in semantics), speak.test.ts, and instructions.test.ts — all pass.
  • pnpm --filter @posthog/{agent,core,workspace-server} typecheck — clean.
  • biome lint on all touched files — clean.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog from a Slack thread

Spoken narration defaulted on for all cloud runs via resolveSpokenNarration
falling back to isCloudRun(meta). That loaded the `speak` tool and its prompt
instructions — and prompted the agent to call it — on headless runs (Slack
threads, Signals scouts) where nothing can play audio, costing tokens with no
listener and regardless of the feature flag, user setting, or ElevenLabs key.

Make narration strictly opt-in: resolveSpokenNarration now returns true only
when a caller explicitly sets spokenNarration. The desktop is the only client
that can play audio and knows the flag/setting/key, so it computes that boolean
and passes it; everything else stays silent and never loads the tool. Also
require an ElevenLabs key alongside the setting on the desktop start/reconnect
paths, and refresh the now-stale comments.

Generated-By: PostHog Code
Task-Id: 7a65b8a4-b69d-472f-9753-67d59715e332
@trunk-io

trunk-io Bot commented Jul 16, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 7e2010c.

@jonmcwest
jonmcwest self-requested a review July 16, 2026 11:17
@jonmcwest
jonmcwest marked this pull request as ready for review July 16, 2026 11:19
Generated-By: PostHog Code
Task-Id: ac30fada-8c29-4973-a253-08d70be31be6
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(agent): allow system voice narration..." | Re-trigger Greptile

Comment thread packages/core/src/sessions/sessionService.ts

@pauldambra pauldambra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note

🤖 Automated comment by QA Swarm — not written by a human

QA Swarm review complete. See inline comments.

Comment thread packages/agent/src/adapters/session-meta.ts
Comment thread packages/ui/src/features/sessions/sessionServiceHost.ts

Copy link
Copy Markdown
Member

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: paul-reviewer, xp-reviewer, security-audit (qa-team unavailable)

Verdict: ⚠️ REQUEST CHANGES

Strict opt-in is correct for headless runs, but interactive cloud propagation and the UI test harness need fixes.

Key findings

  • HIGH: propagate the explicit narration decision to interactive cloud runs.
  • HIGH: bind feature flags in session-service tests and cover flag/opt-in combinations.

Convergence

  • The missing feature-flag test binding was independently found by paul-reviewer and xp-reviewer.

Reviewer summaries

Reviewer Assessment
👤 paul Cloud narration propagation and tests need fixes.
📐 xp Design is simple, but the existing focused test suite breaks.
🛡 security-audit No exploitable vulnerabilities found.

Automated by QA Swarm — not a human review

Generated-By: PostHog Code
Task-Id: ac30fada-8c29-4973-a253-08d70be31be6
Comment thread packages/api-client/src/posthog-client.ts Outdated

Copy link
Copy Markdown
Member

Note

🤖 Automated comment by QA Swarm — not written by a human

Round 2 verdict: ⚠️ REQUEST CHANGES

The earlier test regression is fixed, but the added cloud propagation uses a request field the current backend API does not support. Keep strict opt-in on the supported local transport and leave cloud narration disabled until the server contract exists.


Automated by QA Swarm — not a human review

Generated-By: PostHog Code
Task-Id: ac30fada-8c29-4973-a253-08d70be31be6
meta: SpokenNarrationSource | undefined,
): boolean {
return meta?.spokenNarration ?? isCloudRun(meta);
return meta?.spokenNarration === true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note

🤖 Automated comment by QA Swarm — not written by a human

[convergent: paul-reviewer + xp-reviewer] 🟠 HIGH

Strict opt-in currently makes narration local-only because interactive cloud runs have no backend-supported way to carry the desktop flag + user opt-in decision. Supporting cloud narration requires a coordinated server contract or another explicit transport; otherwise this PR intentionally leaves cloud runs silent.

Copy link
Copy Markdown
Member

Note

🤖 Automated comment by QA Swarm — not written by a human

Round 3 verdict: 💬 APPROVE WITH A DEFERRED PRODUCT DECISION

The local implementation and tests are clean. Interactive cloud narration remains disabled because the current backend has no supported field for the explicit desktop opt-in; enabling it requires coordinated backend work.


Automated by QA Swarm — not a human review

@pauldambra pauldambra added the Stamphog This will request an autostamp by stamphog on small changes label Jul 16, 2026 — with PostHog

@github-actions github-actions 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.

Clean behavioral fix making spoken narration strictly opt-in. The logic is consistent end-to-end: resolver simplified, settings wiring renamed correctly, feature-flag gate added in the host layer, and tests updated. The unresolved bot comment at current head describes the intentional design decision (cloud runs stay silent) rather than a blocking concern.

@pauldambra

Copy link
Copy Markdown
Member

/trunk merge

@pauldambra pauldambra added the Create Release This will trigger a new release label Jul 16, 2026
@trunk-io
trunk-io Bot merged commit 1082a7c into main Jul 16, 2026
42 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/gate-speak-tool-strict-optin branch July 16, 2026 12:13
@github-actions

Copy link
Copy Markdown

Could not verify team-posthog-code membership (GitHub API error). Auto-release is limited to Team PostHog Code members, so this will ship with the next scheduled release instead.

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

Labels

Create Release This will trigger a new release Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants