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
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,14 @@ Three event names, all info-level. `agent_message_sent` and `agent_message_recei

`PHASE3_STEP10_EVENTS` frozenset exposed.

#### Voice modes event (flash / default / think β€” docs/VOICE-MODES-DESIGN.md)

| Event | Source | level | extra fields |
|---|---|---|---|
| `voice_mode_changed` | `codec-voice` | info | `mode` (`flash` \| `default` \| `think`), `via` (`voice` \| `ui`) |

Single-emit, fresh or session cid. Think-mode tool calls need no new events β€” they route through the skill `Tool` wrappers β†’ `run_with_hooks` β†’ existing `tool_call`/`tool_result` envelope.

### Notifications (`~/.codec/notifications.json`)
Four sources can produce notifications: scheduler (crew completion), heartbeat (threshold alert), autopilot (ambient trigger), and Phase 1 Step 3's AskUserQuestion (`type="question"`). All write through `routes/_shared.py:51-127` except AskUserQuestion which writes via `codec_ask_user._write_question_notification`.

Expand Down
45 changes: 45 additions & 0 deletions codec_voice.html
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,20 @@
.modal-overlay.show { display:flex; }
.modal-overlay img { max-width:100%; max-height:90vh; border-radius:var(--radius); border:1px solid var(--border); }
.modal-close { position:fixed; top:16px; right:16px; background:var(--surface); color:var(--text); border:1px solid var(--border); width:32px; height:32px; border-radius:50%; font-size:18px; cursor:pointer; z-index:101; display:flex; align-items:center; justify-content:center; }
/* ── Voice mode pills (flash / default / think) ── */
.mode-row { display:flex; gap:8px; justify-content:center; margin:10px 0 2px; }
.mode-pill {
background:var(--surface-2,#1f1f22); border:1px solid var(--border,#2a2a2f);
color:var(--text-dim,#6a6a70); font-size:11px; font-weight:700;
letter-spacing:1px; padding:5px 14px; border-radius:999px; cursor:pointer;
transition:all .15s; text-transform:uppercase;
}
.mode-pill:hover { color:var(--text,#ececec); border-color:var(--border-hover,#3a3a40); }
.mode-pill.active {
color:#fff; background:var(--accent,#E8711A); border-color:var(--accent,#E8711A);
box-shadow:0 0 12px rgba(232,113,26,.35);
}
.mode-pill.think.active { box-shadow:0 0 12px rgba(232,113,26,.5); }
</style>
</head>
<body id="body">
Expand Down Expand Up @@ -514,6 +528,13 @@ <h1><a href="/" style="color:inherit;text-decoration:none">CODEC</a></h1>
<div class="status" id="statusLabel">Tap to start voice call</div>
<div class="interrupt-hint" id="interruptHint"></div>

<!-- Voice mode: flash / default / think (docs/VOICE-MODES-DESIGN.md) -->
<div class="mode-row" id="modeRow">
<button class="mode-pill" id="modeFlash" onclick="setVoiceMode('flash')" title="Fastest replies β€” trimmed context, one-sentence answers">⚑ Flash</button>
<button class="mode-pill active" id="modeDefault" onclick="setVoiceMode('default')" title="Standard voice chat">Normal</button>
<button class="mode-pill think" id="modeThink" onclick="setVoiceMode('think')" title="Live tool calling β€” lights, music, web, calendar, email β€” with spoken progress">🧠 Think</button>
</div>

<!-- Action row: your turn + nudge -->
<div class="action-row" id="actionRow">
<button class="action-btn your-turn" id="yourTurnBtn" onclick="sendYourTurn()">
Expand Down Expand Up @@ -859,9 +880,16 @@ <h1><a href="/" style="color:inherit;text-decoration:none">CODEC</a></h1>
} else if (msg.type === 'hint') {
var hint = document.getElementById('interruptHint');
if (hint) hint.textContent = msg.text || '';
} else if (msg.type === 'mode') {
updateModePills(msg.mode);
} else if (msg.type === 'status') {
if (msg.status === 'processing') setState('processing');
else if (msg.status === 'analyzing_screen') setState('analyzing_screen');
else if (msg.status === 'tool_running') {
setState('processing');
var sl = document.getElementById('statusLabel');
if (sl) sl.textContent = 'πŸ›  Running tools…';
}
else if (msg.status === 'listening') {
if (!isPlaying) setState('listening');
var hint = document.getElementById('interruptHint');
Expand Down Expand Up @@ -1026,6 +1054,23 @@ <h1><a href="/" style="color:inherit;text-decoration:none">CODEC</a></h1>
}

/* ── "Your Turn" β€” force-send current audio buffer as utterance ── */
/* ── Voice modes (flash / default / think) ── */
var voiceMode = 'default';
function updateModePills(m) {
voiceMode = m || 'default';
var ids = { flash: 'modeFlash', 'default': 'modeDefault', think: 'modeThink' };
Object.keys(ids).forEach(function(k) {
var el = document.getElementById(ids[k]);
if (el) el.classList.toggle('active', k === voiceMode);
});
}
function setVoiceMode(m) {
updateModePills(m); /* optimistic β€” server ack confirms */
if (ws && ws.readyState === WebSocket.OPEN) {
try { ws.send(JSON.stringify({ type: 'mode', mode: m })); } catch (e) {}
}
}

function sendYourTurn() {
if (!ws || ws.readyState !== WebSocket.OPEN || !isInCall) return;
ws.send(JSON.stringify({ type: 'your_turn' }));
Expand Down
Loading