@gotcos/glasses-server 6.24.0 → 6.24.2
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
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
## 6.24.2
|
|
2
|
+
|
|
3
|
+
The empty-recording restart lock, split out of 6.24.1.
|
|
4
|
+
|
|
5
|
+
**Correction to 6.24.1's own entry.** These bullets were written under 6.24.1 while it
|
|
6
|
+
was still unpublished, but it had already gone to the registry by then, so that heading
|
|
7
|
+
described a published artifact that does not contain this code. Verified by downloading
|
|
8
|
+
the published tarball: 6.24.1 carries the `project` field and has no
|
|
9
|
+
`EMPTY_SESSION_STALE_MS`. Moved here rather than left standing.
|
|
10
|
+
|
|
11
|
+
- **A 6-second aborted recording no longer locks the restart for 30 minutes.**
|
|
12
|
+
`getTranscriptionSessionLiveness` gated purely on elapsed time. The 30-minute grace
|
|
13
|
+
exists to protect a real recording that has gone briefly quiet — a backgrounded phone
|
|
14
|
+
buffering to IndexedDB — but a session with NO canonical text has nothing to protect.
|
|
15
|
+
Observed 2026-08-10: `meeting_1786393815060_tp693w` started, received one 5.6s chunk
|
|
16
|
+
that transcribed to empty, stopped 6.2 seconds later, and then blocked Update Server.
|
|
17
|
+
A session idle past `EMPTY_SESSION_STALE_MS` (2 minutes) with zero canonical chunks
|
|
18
|
+
now stops blocking a restart.
|
|
19
|
+
- **Why that cannot reap a live recording.** `lastActivityAt` is bumped on chunk
|
|
20
|
+
ARRIVAL, before any text filtering, so a live recording in a silent room keeps
|
|
21
|
+
arriving every ~10s and never goes idle at all; its silent chunks land in
|
|
22
|
+
`emptyCompletions`, not `chunks`. Gating on emptiness ALONE would kill exactly that
|
|
23
|
+
session, which is why the rule requires emptiness AND idleness. Counted on canonical
|
|
24
|
+
text rather than array length, because the array is sparse and a silent chunk carries
|
|
25
|
+
no text — the precise shape an aborted recording leaves behind.
|
|
26
|
+
|
|
27
|
+
## 6.24.1
|
|
28
|
+
|
|
29
|
+
Surfaces `project` on session rows, so the list can group the way Claude Code's own
|
|
30
|
+
sidebar does.
|
|
31
|
+
|
|
32
|
+
- The indexer was scanning ONE project directory. `PROJECTS_DIR` was hardcoded to
|
|
33
|
+
MU-Chief-Staff, so 17 of 18 project dirs — `cos-glasses-app`, `cos-glasses-server`,
|
|
34
|
+
the 119 projects, the COS examples — were never indexed at all. That is why the
|
|
35
|
+
Sessions list could never match the sidebar: entire projects were absent, not just
|
|
36
|
+
mislabelled. Fixed in session_indexer.py (COS repo); 1,212 sessions now carry a
|
|
37
|
+
project where 1,000 were visible before.
|
|
38
|
+
- `project` comes from the `cwd` on the session records, not from decoding the
|
|
39
|
+
directory name. Claude Code replaces path separators with dashes and a real dash is
|
|
40
|
+
indistinguishable from a separator afterwards, so
|
|
41
|
+
`-Users-ukaoma-Documents-GitHub-Ukaoma-Chief-Of-Staff-MU-Chief-Staff` decoded to
|
|
42
|
+
"Ukaoma-Chief-Of-Staff-MU-Chief-Staff" where the sidebar says "MU-Chief-Staff".
|
|
43
|
+
|
|
1
44
|
## 6.24.0
|
|
2
45
|
|
|
3
46
|
The Sessions tab stops 404ing, and a read-only presence view of Claude Code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotcos/glasses-server",
|
|
3
|
-
"version": "6.24.
|
|
3
|
+
"version": "6.24.2",
|
|
4
4
|
"description": "COS Glasses \u2014 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": {
|
|
@@ -54,6 +54,8 @@ export interface SessionEntry {
|
|
|
54
54
|
display_label: string
|
|
55
55
|
/** True when the harness opened this session rather than a person. */
|
|
56
56
|
machine_spawned: boolean
|
|
57
|
+
/** Project name, matching the group heading in Claude Code's own sidebar. */
|
|
58
|
+
project: string
|
|
57
59
|
glasses_session_id: string
|
|
58
60
|
slug: string
|
|
59
61
|
created: string
|
|
@@ -210,6 +212,7 @@ sessionIndexRouter.get('/session-index', async (req, res) => {
|
|
|
210
212
|
display_label: str(entry.display_label) || str(entry.custom_title)
|
|
211
213
|
|| str(entry.first_prompt) || str(entry.slug) || entry.session_id.slice(0, 8),
|
|
212
214
|
machine_spawned: entry.machine_spawned === true,
|
|
215
|
+
project: str(entry.project),
|
|
213
216
|
domain,
|
|
214
217
|
device_id: deviceId,
|
|
215
218
|
})
|
|
@@ -260,6 +263,7 @@ sessionIndexRouter.get('/session-index/:session_id', async (req, res) => {
|
|
|
260
263
|
display_label: str(entry.display_label) || str(entry.custom_title)
|
|
261
264
|
|| str(entry.first_prompt) || str(entry.slug) || entry.session_id.slice(0, 8),
|
|
262
265
|
machine_spawned: entry.machine_spawned === true,
|
|
266
|
+
project: str(entry.project),
|
|
263
267
|
tools_used: (entry.tools_used && typeof entry.tools_used === 'object') ? entry.tools_used : {},
|
|
264
268
|
files_touched: Array.isArray(entry.files_touched) ? entry.files_touched : [],
|
|
265
269
|
domain: str(entry.domain, 'unknown') || 'unknown',
|
|
@@ -387,6 +387,26 @@ export function getActiveTranscriptionSessionCount(): number {
|
|
|
387
387
|
*/
|
|
388
388
|
export const RECORDING_SESSION_STALE_MS = STRANDED_STALE_MS
|
|
389
389
|
|
|
390
|
+
/**
|
|
391
|
+
* How long an EMPTY session may hold the restart lock.
|
|
392
|
+
*
|
|
393
|
+
* The 30-minute grace above protects a real recording that has gone briefly quiet — a
|
|
394
|
+
* backgrounded phone buffering to IndexedDB, a network drop, a long pause. A session
|
|
395
|
+
* that has produced NO canonical text has nothing to protect, so it must not lock
|
|
396
|
+
* Install, Repair, Restart and Update Server for half an hour.
|
|
397
|
+
*
|
|
398
|
+
* Observed 2026-08-10: meeting_1786393815060_tp693w started, took ONE 5.6s chunk that
|
|
399
|
+
* transcribed to empty, stopped 6.2s later, then blocked every restart for 30 minutes.
|
|
400
|
+
*
|
|
401
|
+
* WHY THIS CANNOT REAP A LIVE RECORDING. `lastActivityAt` is bumped on chunk ARRIVAL
|
|
402
|
+
* in processStreamChunk, before any text filtering, so a live recording in a silent
|
|
403
|
+
* room keeps arriving every ~10s and never goes idle — its silent chunks land in
|
|
404
|
+
* `emptyCompletions`, not `chunks`. Gating on emptiness ALONE would kill exactly that
|
|
405
|
+
* session; gating on emptiness AND idleness cannot. Two minutes is comfortably more
|
|
406
|
+
* than one chunk interval, so a recording that has just started is safe.
|
|
407
|
+
*/
|
|
408
|
+
export const EMPTY_SESSION_STALE_MS = 2 * 60_000
|
|
409
|
+
|
|
390
410
|
export interface TranscriptionSessionLiveness {
|
|
391
411
|
/** Sessions still plausibly recording. These block a restart. */
|
|
392
412
|
live: number
|
|
@@ -479,8 +499,12 @@ export function getTranscriptionSessionLiveness(now = Date.now()): Transcription
|
|
|
479
499
|
const staleSessions: TranscriptionSessionLiveness['staleSessions'] = []
|
|
480
500
|
for (const [sessionId, session] of sessions) {
|
|
481
501
|
const silentForMs = now - session.lastActivityAt
|
|
482
|
-
|
|
483
|
-
|
|
502
|
+
// Canonical text, not array length: the array is sparse and silent chunks carry no
|
|
503
|
+
// text, which is exactly the shape an aborted recording leaves behind.
|
|
504
|
+
const canonicalChunks = session.chunks.filter(chunk => chunk && chunk.text).length
|
|
505
|
+
const emptyAndIdle = canonicalChunks === 0 && silentForMs >= EMPTY_SESSION_STALE_MS
|
|
506
|
+
if (silentForMs >= RECORDING_SESSION_STALE_MS || emptyAndIdle) {
|
|
507
|
+
staleSessions.push({ sessionId, silentForMs, chunks: canonicalChunks })
|
|
484
508
|
} else {
|
|
485
509
|
live++
|
|
486
510
|
}
|