@gotcos/glasses-server 6.8.0 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +3 -1
- package/package.json +1 -1
- package/server/lib/display-bus.ts +1 -1
- package/server/routes/prompt-drafts.ts +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.9.0
|
|
4
|
+
|
|
5
|
+
Live recoverable prompt transcription for COS Glasses builds 200+.
|
|
6
|
+
|
|
7
|
+
- **Words appear while speaking.** After each audio chunk is durably acknowledged,
|
|
8
|
+
its sanitized fast/local transcript is published on the existing authenticated,
|
|
9
|
+
replayable display stream as `prompt_transcript`; the phone/G2 client can fill
|
|
10
|
+
the Listening body without adding another recorder, polling loop, or ASR job.
|
|
11
|
+
- **Recovery remains authoritative.** The event is optional presentation state.
|
|
12
|
+
Stored WAV chunks, final HQ transcription, glossary cleanup, editing, retry,
|
|
13
|
+
and send behavior remain unchanged and continue even if no display client is
|
|
14
|
+
connected.
|
|
15
|
+
- **Stale retries cannot repaint.** The server rechecks the exact draft, chunk
|
|
16
|
+
index, and audio bytes after warm transcription. Replaced audio never emits
|
|
17
|
+
its obsolete words, while client-side draft scoping, ordering, and replay
|
|
18
|
+
deduplication handle reconnects safely.
|
|
19
|
+
- **Public boundary retained.** This release adds no private COS paths, personal
|
|
20
|
+
data, LaunchAgent controls, remote restart authority, or machine-management
|
|
21
|
+
endpoints.
|
|
22
|
+
|
|
3
23
|
## 6.8.0
|
|
4
24
|
|
|
5
25
|
Public-safe meeting finalization for COS Glasses build 199.
|
package/README.md
CHANGED
|
@@ -64,7 +64,9 @@ The built-in IP allowlist blocks public-internet traffic regardless.
|
|
|
64
64
|
- Send phone photos with queued prompts, and review assistant-selected generated,
|
|
65
65
|
research, or explicitly used email images in Messages and on the G2 lens
|
|
66
66
|
- Recover long voice prompts after phone, network, or server interruptions. Audio
|
|
67
|
-
chunks are saved before transcription and retained locally for 72 hours.
|
|
67
|
+
chunks are saved before transcription and retained locally for 72 hours. On
|
|
68
|
+
compatible app builds, their warm transcript also appears live while speaking;
|
|
69
|
+
final HQ transcription remains authoritative.
|
|
68
70
|
- Live voice capture + transcription during meetings
|
|
69
71
|
- Local whisper.cpp transcription (free) with OpenAI fallback (optional)
|
|
70
72
|
- Tasks / calendar / people context **if** you run the
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ const bus = new EventEmitter()
|
|
|
8
8
|
bus.setMaxListeners(20) // Multiple glasses clients
|
|
9
9
|
|
|
10
10
|
export interface DisplayEvent {
|
|
11
|
-
type: 'chunk' | 'done' | 'error' | 'tool_status' | 'start' | 'session_restore' | 'transcript_chunk' | 'recording_start' | 'recording_stop' | 'coaching_nudge'
|
|
11
|
+
type: 'chunk' | 'done' | 'error' | 'tool_status' | 'start' | 'session_restore' | 'transcript_chunk' | 'prompt_transcript' | 'recording_start' | 'recording_stop' | 'coaching_nudge'
|
|
12
12
|
data: Record<string, unknown>
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -38,6 +38,7 @@ import { createBreaker } from '../lib/claude-circuit.js'
|
|
|
38
38
|
import { logTokenAudit } from '../lib/token-audit.js'
|
|
39
39
|
import { atomicWriteFileSync } from '../lib/atomic-fs.js'
|
|
40
40
|
import { dataPath } from '../lib/data-dir.js'
|
|
41
|
+
import { emitDisplay } from '../lib/display-bus.js'
|
|
41
42
|
|
|
42
43
|
export const promptDraftsRouter = Router()
|
|
43
44
|
|
|
@@ -234,7 +235,17 @@ promptDraftsRouter.post('/prompt-drafts/:draftId/chunks', async (req, res) => {
|
|
|
234
235
|
const nextTotal = Object.values(before.chunkBytes).reduce((sum, bytes) => sum + bytes, 0) - existingBytes + audio.length
|
|
235
236
|
if (nextTotal > MAX_DRAFT_BYTES) return res.status(413).json({ error: 'prompt draft too large' })
|
|
236
237
|
const meta = await savePromptDraftChunk(req.params.draftId, chunkIndex, audio)
|
|
237
|
-
warmTail = warmTail.then(
|
|
238
|
+
warmTail = warmTail.then(async () => {
|
|
239
|
+
const text = await transcribeChunk(req.params.draftId, chunkIndex, audio, 'fast', 'warm')
|
|
240
|
+
// The durability ACK above remains immediate. Publish the optional warm
|
|
241
|
+
// transcript only after rechecking that this exact audio still owns the
|
|
242
|
+
// chunk index; a retry/replacement must never paint stale words.
|
|
243
|
+
if (!isCurrentChunk(req.params.draftId, chunkIndex, audio)) return
|
|
244
|
+
emitDisplay({
|
|
245
|
+
type: 'prompt_transcript',
|
|
246
|
+
data: { draftId: req.params.draftId, chunkIndex, text },
|
|
247
|
+
})
|
|
248
|
+
}).catch(err => {
|
|
238
249
|
console.warn(`[prompt-draft] warm transcription failed ${req.params.draftId}/${chunkIndex}: ${err.message}`)
|
|
239
250
|
})
|
|
240
251
|
res.json({ draftId: meta.draftId, chunkIndex, acked: true, receivedChunkIndexes: meta.receivedChunkIndexes, chunkBytes: meta.chunkBytes[String(chunkIndex)] ?? audio.length, transcriptPending: true, expiresAt: meta.expiresAt })
|