@gotcos/glasses-server 6.45.5 → 6.46.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 +12 -0
- package/package.json +2 -2
- package/server/index.ts +2 -0
- package/server/lib/cos-operations-meetings.ts +1 -1
- package/server/lib/g2-ops-handoff.ts +27 -5
- package/server/lib/held-naming-batches.ts +527 -0
- package/server/lib/held-voice-groups.ts +103 -12
- package/server/lib/meeting-batch-persistence.ts +1 -0
- package/server/lib/meeting-corrections.ts +43 -5
- package/server/lib/meeting-speaker-labels.ts +192 -0
- package/server/lib/meeting-speaker-review.ts +18 -5
- package/server/lib/meeting-sync-lock.ts +26 -0
- package/server/lib/speaker-embeddings.ts +1 -1
- package/server/lib/speaker-rename-fanout.ts +91 -177
- package/server/lib/training-audio-save.ts +24 -0
- package/server/lib/voice-directory.ts +2 -1
- package/server/lib/voice-enrolment-selection.ts +25 -0
- package/server/lib/voice-profile-store.ts +8 -3
- package/server/routes/meeting.ts +5 -1
- package/server/routes/transcribe-stream.ts +13 -41
- package/server/routes/voice.ts +29 -43
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 6.46.0
|
|
2
|
+
|
|
3
|
+
Name held voice samples and preview the meeting labels they will change. Paired Control build: 0.5.223.
|
|
4
|
+
|
|
5
|
+
- Individual held samples can suggest an existing voice using the same second-best-sample scoring as groups, with a clear margin and owner-proximity caution. Suggestions remain proposals.
|
|
6
|
+
- Naming returns a server-stored preview first. Apply checks the preview, voice store, raw chunk map and each meeting copy again, holds the COS sync lock where available, and writes chunk-scoped confirmations. Wider matches must agree with the named samples and the updated voice profile in their source meeting.
|
|
7
|
+
- Corrected labels reach HQ word speakers, live chunks and operations copies. Transcript words remain unchanged; only fully aligned turn labels change. Newer speaker corrections survive re-import. Search catches up through the existing indexing queue; graph staleness is explicit.
|
|
8
|
+
- Each naming has durable copy receipts and Undo. Interrupted work requires review. Undo restores eligible labels, retains enrolled voice samples, and never resurrects deleted audio. Held audio is deleted after successful meeting receipts.
|
|
9
|
+
- Existing-name matching resolves stored spelling before preview. Owner naming requires an explicit acknowledgement and stronger acoustic match. Older clients cannot apply the new naming contract without a preview.
|
|
10
|
+
- Voice-profile merges retain stronger source evidence before applying oldest-first eviction within a source tier. Training audio has its own retention capacity, independent of profile size, and freed slots are usable without a restart.
|
|
11
|
+
- Compatibility: 6.45.5 safely ignores chunk-scoped confirmations. It does not understand reverted batch history; keep 6.46.0 for naming recovery and Undo history after rollback.
|
|
12
|
+
|
|
1
13
|
## 6.45.5
|
|
2
14
|
|
|
3
15
|
Sessions carry their last real activity and last tool (2026-09-13), for the phone Sessions list in COS Glasses 6.9.470.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotcos/glasses-server",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "COS Glasses
|
|
3
|
+
"version": "6.46.0",
|
|
4
|
+
"description": "COS Glasses \u2014 self-hosted AI heads-up-display server for Even G2 smart glasses, powered by Claude Code, Codex, Cursor Agent CLI, or local Ollama",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"glasses-server": "bin/cli.cjs",
|
package/server/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ import { promptDraftsRouter } from './routes/prompt-drafts.js'
|
|
|
51
51
|
import { cliDebugRouter } from './routes/cli-debug.js'
|
|
52
52
|
import { maintenanceRouter } from './routes/maintenance.js'
|
|
53
53
|
import { ttsRouter } from './routes/tts.js'
|
|
54
|
+
import { recoverInterruptedNaming } from './lib/held-naming-batches.js'
|
|
54
55
|
import { voiceRouter } from './routes/voice.js'
|
|
55
56
|
import { glossaryRouter } from './routes/glossary.js'
|
|
56
57
|
import { handoffsRouter } from './routes/handoffs.js'
|
|
@@ -684,6 +685,7 @@ app.use('/api', promptDraftsRouter)
|
|
|
684
685
|
app.use('/api', cliDebugRouter)
|
|
685
686
|
app.use('/api', maintenanceRouter)
|
|
686
687
|
app.use('/api', ttsRouter)
|
|
688
|
+
recoverInterruptedNaming()
|
|
687
689
|
app.use('/api', voiceRouter)
|
|
688
690
|
app.use('/api', glossaryRouter)
|
|
689
691
|
app.use('/api', handoffsRouter)
|
|
@@ -462,7 +462,7 @@ export function findCosOperationsMeetingBySessionId(sessionId: string): {
|
|
|
462
462
|
const monthDir = join(meetingsBase, month)
|
|
463
463
|
let sidecars: string[]
|
|
464
464
|
try {
|
|
465
|
-
sidecars = readdirSync(monthDir).filter(f => f.endsWith('.g2-chunks.json')).sort().reverse()
|
|
465
|
+
sidecars = readdirSync(monthDir).filter(f => f.endsWith('.g2-chunks.json') && !/ \d+(\.[A-Za-z0-9-]+)*\.json$/.test(f)).sort().reverse()
|
|
466
466
|
} catch { continue }
|
|
467
467
|
|
|
468
468
|
for (const sidecarName of sidecars) {
|
|
@@ -23,6 +23,9 @@ const DOMAIN_REVIEW_MARKER = '<!-- g2-needs-domain-review -->'
|
|
|
23
23
|
const HQ_PENDING_MARKER = '<!-- g2-hq-state: pending -->'
|
|
24
24
|
const OPERATIONS_OWNED_SIDECAR_FIELDS = new Set([
|
|
25
25
|
'blended_into',
|
|
26
|
+
'claimed_parent',
|
|
27
|
+
'correctionRevision',
|
|
28
|
+
'labelsNewerThanGraph',
|
|
26
29
|
'dedupEvidence',
|
|
27
30
|
'enrichmentState',
|
|
28
31
|
'finalPath',
|
|
@@ -124,6 +127,12 @@ function parseSidecar(path: string): Record<string, unknown> | null {
|
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
|
|
130
|
+
export function g2Revision(value: unknown): number {
|
|
131
|
+
if (value === undefined || value === null) return 0
|
|
132
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) throw new Error('Invalid G2 revision')
|
|
133
|
+
return value
|
|
134
|
+
}
|
|
135
|
+
|
|
127
136
|
/** Merge server-owned capture truth while preserving operations-owned match state. */
|
|
128
137
|
export function mergeG2OperationsSidecar(
|
|
129
138
|
sourcePath: string,
|
|
@@ -139,8 +148,8 @@ export function mergeG2OperationsSidecar(
|
|
|
139
148
|
throw new Error(`Refusing G2 sidecar merge across sessions (${existingSession} != ${sourceSession})`)
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
const priorRevision =
|
|
143
|
-
const nextRevision = Math.max(
|
|
151
|
+
const priorRevision = g2Revision(existing.lifecycleRevision)
|
|
152
|
+
const nextRevision = Math.max(g2Revision(options.revision), g2Revision(source.lifecycleRevision))
|
|
144
153
|
if (Number.isFinite(priorRevision) && Number.isFinite(nextRevision) && nextRevision < priorRevision) {
|
|
145
154
|
throw new Error(`Refusing regressing G2 sidecar revision ${nextRevision} < ${priorRevision}`)
|
|
146
155
|
}
|
|
@@ -149,10 +158,19 @@ export function mergeG2OperationsSidecar(
|
|
|
149
158
|
for (const field of OPERATIONS_OWNED_SIDECAR_FIELDS) {
|
|
150
159
|
if (Object.prototype.hasOwnProperty.call(existing, field)) merged[field] = existing[field]
|
|
151
160
|
}
|
|
161
|
+
const correctionFields = ['chunks', 'chunkEntries', 'speakers', 'batchSegments', 'batchApplied', 'correctionRevision', 'labelsNewerThanGraph']
|
|
162
|
+
if (g2Revision(existing.correctionRevision) > g2Revision(source.correctionRevision)) {
|
|
163
|
+
for (const field of correctionFields) {
|
|
164
|
+
if (Object.prototype.hasOwnProperty.call(existing, field)) merged[field] = existing[field]
|
|
165
|
+
else delete merged[field]
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
merged.correctionRevision = Math.max(g2Revision(existing.correctionRevision), g2Revision(source.correctionRevision))
|
|
169
|
+
merged.labelsNewerThanGraph = existing.labelsNewerThanGraph === true || source.labelsNewerThanGraph === true
|
|
152
170
|
merged.lifecycleRevision = Math.max(priorRevision || 0, nextRevision || 0)
|
|
153
|
-
const inferredFinalHqState =
|
|
171
|
+
const inferredFinalHqState = merged.batchApplied === true
|
|
154
172
|
? 'accepted'
|
|
155
|
-
:
|
|
173
|
+
: merged.batchQualityReport
|
|
156
174
|
? 'rejected'
|
|
157
175
|
: 'unavailable'
|
|
158
176
|
merged.hqState = options.hqState
|
|
@@ -184,7 +202,11 @@ export function stageRecordingIntoOperations(
|
|
|
184
202
|
const destDir = join(operationsDir, 'personal', 'meetings', month)
|
|
185
203
|
mkdirSync(destDir, { recursive: true })
|
|
186
204
|
const destPath = join(destDir, basename(localMeetingPath))
|
|
187
|
-
const
|
|
205
|
+
const sourceSidecar = parseSidecar(localMeetingPath.replace(/\.md$/, '.g2-chunks.json'))
|
|
206
|
+
const destinationSidecar = parseSidecar(destPath.replace(/\.md$/, '.g2-chunks.json'))
|
|
207
|
+
const keepMarkdown = g2Revision(destinationSidecar?.correctionRevision) > g2Revision(sourceSidecar?.correctionRevision)
|
|
208
|
+
if (keepMarkdown && !existsSync(destPath)) throw new Error('Corrected operations markdown missing; refusing stale import')
|
|
209
|
+
const patched = keepMarkdown ? readFileSync(destPath, 'utf8') : patchRecordingForG2Pipeline(readFileSync(localMeetingPath, 'utf8'), options)
|
|
188
210
|
|
|
189
211
|
const stem = basename(localMeetingPath, '.md')
|
|
190
212
|
const localDir = dirname(localMeetingPath)
|