@gotcos/glasses-server 6.21.4 → 6.21.6

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/.env.example CHANGED
@@ -28,13 +28,13 @@ BIND_HOST=0.0.0.0
28
28
  # server after Wi-Fi/Tailscale changes and process restarts.
29
29
  # COS_SERVER_INSTANCE_ID_PATH=/path/to/server-instance-id
30
30
 
31
- # Build 204+ can opt into server-owned durable query jobs. The server fsyncs an
31
+ # Build 204+ uses server-owned durable query jobs by default. The server fsyncs an
32
32
  # accepted prompt before returning 202, then keeps provider work running if the
33
33
  # phone backgrounds, reloads, or changes networks. Reopening COS reattaches to
34
- # the same job. Set to 0 (or remove) to send NEW prompts through the legacy
34
+ # the same job. Set to 0 to send NEW prompts through the legacy
35
35
  # streaming route; already accepted durable jobs remain readable/cancellable
36
36
  # and drain safely.
37
- # COS_DURABLE_QUERY_JOBS=1
37
+ # COS_DURABLE_QUERY_JOBS=0
38
38
 
39
39
  # ── THE LLM (chat) ──────────────────────────────────────────────────────
40
40
  # Chat runs through your LOCAL agent CLI — NOT an API key:
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 6.21.6
2
+
3
+ - Make server-owned durable query jobs the default so accepted replies keep
4
+ running through phone backgrounding, WebView reloads, and network handoffs.
5
+ - Preserve `COS_DURABLE_QUERY_JOBS=0` as the machine-wide rollback. Existing
6
+ accepted jobs remain recoverable and cancellable while new prompts use the
7
+ legacy streaming path.
8
+ - Publish the capability truth through authenticated health/model surfaces so
9
+ COS Control and the companion can independently enforce machine and device
10
+ preferences without changing the proven two-step cancel gesture.
11
+
12
+ ## 6.21.5
13
+
14
+ - Drop the narrow Large-v3 stock-caption artifact family beginning with
15
+ “closed captioning provided by” from prompt dictation.
16
+ - When that exact HQ artifact occurs, retain the already-validated Fast
17
+ transcript for the same audio and report the result as degraded instead of
18
+ saving either the caption credit or an empty gap. All other HQ and no-speech
19
+ paths are unchanged.
20
+
1
21
  ## 6.21.4
2
22
 
3
23
  - Preserve validated phone-photo references on Claude and Codex conversation
package/README.md CHANGED
@@ -102,8 +102,8 @@ range is the exact Tailscale/CGNAT allocation (`100.64.0.0/10`), not all of
102
102
  ## What it does
103
103
 
104
104
  - Ask anything, get a streamed answer on the lens (`/api/query`, `/v1/chat/completions`)
105
- - With COS Glasses build 204+, opt into server-owned durable queries with
106
- `COS_DURABLE_QUERY_JOBS=1`: accepted work survives phone backgrounding,
105
+ - With COS Glasses build 204+, server-owned durable queries are on by default:
106
+ accepted work survives phone backgrounding,
107
107
  WebView reloads, and network handoffs, then reattaches without duplicate work
108
108
  or duplicate replies
109
109
  - Choose Opus, Fable, Sonnet, GPT Frontier, GPT Balanced, Composer 2.5 Fast, or
@@ -174,8 +174,8 @@ in the managed CLI working directory),
174
174
  `COS_CURSOR_PERSIST_SESSIONS=0` (disable Cursor session resume),
175
175
  `COS_WEATHER_DEFAULT_LAT` / `COS_WEATHER_DEFAULT_LON` /
176
176
  `COS_WEATHER_DEFAULT_CITY` (optional home fallback when phone GPS is denied),
177
- `COS_SCRIPTS_DIR` (full pipeline), `COS_DURABLE_QUERY_JOBS=1` (build 204+
178
- server-owned query recovery), and `COS_MEDIA_ROOT` (optional image-store
177
+ `COS_SCRIPTS_DIR` (full pipeline), `COS_DURABLE_QUERY_JOBS=0` (optional
178
+ machine-wide rollback for build 204+ server-owned query recovery), and `COS_MEDIA_ROOT` (optional image-store
179
179
  location; default `~/.cos-glasses/data/media`). Your name + transcription vocabulary live in
180
180
  `~/.cos-glasses/.cos-profile.json` (see `.cos-profile.example.json`).
181
181
  Factory example values are ignored; add the real names, companies, acronyms,
@@ -338,10 +338,10 @@ BIND_HOST=0.0.0.0 npm run start:server
338
338
  only when OpenAI playback is intentionally configured.
339
339
  - *Photos unavailable?* — install `ffmpeg`, restart the server, and confirm `/api/health` reports `features.mediaProcessingReady: true`.
340
340
  - *Prompt recovery unavailable?* — update with `npx --yes @gotcos/glasses-server@latest`, then confirm `/api/health` reports `features.promptRecovery: true`.
341
- - *Durable query recovery unavailable?* — build 204+ requires server 6.10.0+ and
342
- `COS_DURABLE_QUERY_JOBS=1`. Restart once, then confirm `/api/health` reports
341
+ - *Durable query recovery unavailable?* — build 204+ requires server 6.10.0+.
342
+ Restart once, then confirm `/api/health` reports
343
343
  `features.durableQueryJobs: true`, protocol `1`, and state `ready`. To roll
344
- back, remove the flag; accepted jobs still drain while new prompts use legacy streaming.
344
+ back, set `COS_DURABLE_QUERY_JOBS=0`; accepted jobs still drain while new prompts use legacy streaming.
345
345
  - *Offline meeting recovery unavailable?* — build 209+ requires server 6.11.0+.
346
346
  Restart once, then confirm `/api/health` reports
347
347
  `features.localFirstMeetings: true` and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.21.4",
3
+ "version": "6.21.6",
4
4
  "description": "COS Glasses — self-hosted AI heads-up-display server for Even G2 smart glasses, powered by Claude Code, Codex, or Cursor Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/server/index.ts CHANGED
@@ -432,23 +432,22 @@ listenRequiredServers(listeners).then(() => {
432
432
  console.log(`[startup] Silero VAD: ${vadOk ? 'active' : 'disabled (model not found)'}`)
433
433
  }
434
434
 
435
- // Durable recovery and state-mutating background work remain closed until an
436
- // authenticated controller release. The lifecycle callback is delivered for
437
- // both an already-open fresh boot and a later cross-boot release.
435
+ // Durable recovery starts with the admitted runtime by default. A literal
436
+ // COS_DURABLE_QUERY_JOBS=0 is the machine-wide rollback.
438
437
  let admittedRuntimeStarted = false
439
438
  const startAdmittedRuntime = () => {
440
439
  if (admittedRuntimeStarted) return
441
440
  admittedRuntimeStarted = true
442
441
 
443
442
  void initQueryJobRuntime().then(health => {
444
- if (process.env.COS_DURABLE_QUERY_JOBS === '1') {
443
+ if (process.env.COS_DURABLE_QUERY_JOBS !== '0') {
445
444
  console.log(`[COS API] Durable query jobs: ${health.store.state} · ${health.store.retainedIdentities} retained`)
446
445
  } else {
447
- console.log('[COS API] Durable query jobs: disabled (set COS_DURABLE_QUERY_JOBS=1 to enable)')
446
+ console.log('[COS API] Durable query jobs: disabled by COS_DURABLE_QUERY_JOBS=0')
448
447
  }
449
448
  }).catch(error => {
450
449
  // The store remains degraded and rejects admission. Legacy /api/query is
451
- // still mounted, so disabling the feature flag is an immediate rollback.
450
+ // still mounted, so the kill switch is an immediate rollback.
452
451
  console.error('[COS API] Durable query-job store unavailable:', error)
453
452
  })
454
453
 
@@ -14,8 +14,11 @@
14
14
  import { getNegativeRules, getVocabulary, getOwnerName, getWhisperCorrections } from './profile.js'
15
15
 
16
16
  // ── Whole-chunk silence hallucinations ─────────────────────────────────────
17
+ const CAPTION_CREDIT_HALLUCINATION = /^\s*(?:closed\s+)?caption(?:ing|s)?\s+(?:is\s+)?provided\s+by\b/i
18
+
17
19
  const KNOWN_HALLUCINATIONS = [
18
20
  /^subtitles?\s+by\b/i,
21
+ CAPTION_CREDIT_HALLUCINATION,
19
22
  /\blike\s+and\s+subscribe\b/i,
20
23
  /\bthanks?\s+for\s+watching\b/i,
21
24
  /\bplease\s+subscribe\b/i,
@@ -27,6 +30,13 @@ const KNOWN_HALLUCINATIONS = [
27
30
  /^\s*♪+\s*[^♪\n]{0,40}\s*♪+\s*$/,
28
31
  ]
29
32
 
33
+ /** Narrow stock-caption detector used to retain a valid Fast draft when an HQ
34
+ * decode leaks a caption credit. It intentionally requires the phrase to begin
35
+ * the chunk so normal questions about closed captioning remain untouched. */
36
+ export function isCaptionCreditHallucination(text: string): boolean {
37
+ return CAPTION_CREDIT_HALLUCINATION.test(text)
38
+ }
39
+
30
40
  // ── Inline "Name:" detector (session-aware in streaming, static in one-shot) ──
31
41
  const INLINE_HALLUCINATION_THRESHOLD = 3
32
42
  // Seed list of known Whisper "Name:" training artifacts. Empty by default — the
@@ -1,12 +1,12 @@
1
1
  import { QUERY_JOB_PROTOCOL_VERSION } from './query-job-types.js'
2
2
 
3
3
  /**
4
- * Durable jobs ship dark. The private canary enables them explicitly with
5
- * COS_DURABLE_QUERY_JOBS=1; removing the flag is an immediate server-side
6
- * rollback that leaves the legacy /api/query path untouched.
4
+ * Durable jobs are the public default. COS_DURABLE_QUERY_JOBS=0 is the
5
+ * machine-wide rollback; every other value (including unset) keeps the
6
+ * capability available. COS Control owns the user-facing policy switch.
7
7
  */
8
8
  export function durableQueryJobsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
9
- return env.COS_DURABLE_QUERY_JOBS === '1'
9
+ return env.COS_DURABLE_QUERY_JOBS !== '0'
10
10
  }
11
11
 
12
12
  export function durableQueryJobsCapability(): {
@@ -29,6 +29,7 @@ import {
29
29
  stripInlineHallucinations,
30
30
  stripPromptDictationArtifacts,
31
31
  isFullHallucination,
32
+ isCaptionCreditHallucination,
32
33
  isBrandUrlOnly,
33
34
  clearSessionHallucinationState,
34
35
  applyNegativeRules,
@@ -250,6 +251,35 @@ async function transcribeChunk(draftId: string, chunkIndex: number, audio: Buffe
250
251
  return text
251
252
  } catch (err) {
252
253
  if (err instanceof NoSpeechDetectedError) {
254
+ // Large-v3 can rarely replace a clipped but valid phrase with a stock
255
+ // caption credit. Keep the already-decoded Fast text for this one
256
+ // recognized failure shape instead of persisting either the artifact
257
+ // or an empty gap. Other no-speech results retain existing behavior.
258
+ if (mode === 'hq' && isCaptionCreditHallucination(err.rawText)) {
259
+ const fastJob = modeQualityJobs.get(modeQualityKey(draftId, chunkIndex, 'fast', hash))
260
+ if (fastJob) {
261
+ try { await fastJob } catch { /* normal no-speech path below */ }
262
+ }
263
+ const fast = loadPromptDraftMeta(draftId)?.warmTranscripts?.[String(chunkIndex)]
264
+ if (
265
+ fast
266
+ && fast.hash === hash
267
+ && fast.requestedMode === 'fast'
268
+ && fast.actualQuality === 'fast'
269
+ && fast.text.trim()
270
+ ) {
271
+ if (purpose === 'final') {
272
+ await markPromptDraftChunkTranscript(draftId, chunkIndex, {
273
+ ...fast,
274
+ requestedMode: 'hq',
275
+ degraded: true,
276
+ degradationReason: 'hq_caption_credit_hallucination',
277
+ }, 'final')
278
+ }
279
+ console.warn(`[prompt-draft] dropped HQ caption-credit hallucination ${draftId}/${chunkIndex}; retained Fast text`)
280
+ return fast.text
281
+ }
282
+ }
253
283
  await markPromptDraftChunkTranscript(draftId, chunkIndex, {
254
284
  text: '', hash, requestedMode: mode, actualQuality: mode, backend: 'no-speech', degraded: false,
255
285
  }, purpose)