feat: unlock voice audio + terse demo-style collision alerts

Voice generation, critical escalation, and audio publish all worked, but
participants heard nothing: browsers block autoplay of the agent's audio
track until a user gesture. Add the canonical LiveKit unlock — listen for
AudioPlaybackStatusChanged and show an "Enable sound" button that calls
room.startAudio() so PodMan's voice cues are actually heard.

Also make collision alerts short and direct instead of chatty AI prose:
  card:  "Conflict: ram + yahya both on README.md (unpushed). Seen before."
  voice: "Conflict. ram + yahya, both on README.md."

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaCFWMkYQmTcuPsxaaACft
This commit is contained in:
Ramis
2026-06-27 21:08:21 -07:00
parent 16e959072b
commit 4142876b23
2 changed files with 46 additions and 9 deletions
+11 -9
View File
@@ -71,15 +71,17 @@ export class PodMan {
await recordCollision(collision);
const action = preferredAction(collision, prior);
const names = collision.engineers.join(' and ');
const names = collision.engineers.join(' + ');
const shortFile = collision.file.split('/').pop() ?? collision.file;
// Terse, demo-centered alert — short and direct, not chatty AI prose.
const message =
`${names} are both editing ${collision.file}` +
(collision.githubState?.unpushed ? ' and one has unpushed changes.' : '.') +
(prior?.priorOutcome?.accepted
? ` I've seen this conflict pattern before; last time the team accepted the ${prior.priorIntervention?.suggestedAction.kind.replaceAll('_', ' ') ?? 'suggested'} action.`
: prior
? ` I've seen this conflict pattern before.`
: '');
`Conflict: ${names} both on ${shortFile}` +
(collision.githubState?.unpushed ? ' (unpushed).' : '.') +
(prior ? ' Seen before.' : '');
// Spoken line is even shorter so the voice cue lands fast on stage.
const voiceLine = `Conflict. ${names}, both on ${shortFile}.`;
const intervention: Intervention = {
id: `int_${Date.now()}`,
@@ -106,6 +108,6 @@ export class PodMan {
topic: DATA_TOPIC,
});
await publishHermesMessage(this.room, collision, intervention);
if (collision.severity === 'critical') await speak(this.room, message);
if (collision.severity === 'critical') await speak(this.room, voiceLine);
}
}