@gotcos/glasses-server 6.44.16 → 6.45.1
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 +77 -51
- package/README.md +9 -0
- package/bin/memory-setup.cjs +55 -0
- package/memory-runtime/cos-memory-runtime-0.1.0.tar.gz +0 -0
- package/memory-runtime/runtime.json +7 -0
- package/package.json +7 -4
- package/server/index.ts +4 -4
- package/server/lib/adaptive-playback-audio.ts +1 -1
- package/server/lib/agent-session-store.ts +139 -28
- package/server/lib/archive-search.ts +1 -1
- package/server/lib/attached-provider-adapter.ts +1 -1
- package/server/lib/attached-workspace.ts +3 -3
- package/server/lib/chunk-embedding-store.ts +1 -1
- package/server/lib/context-files.ts +1 -1
- package/server/lib/cos-context-browser.ts +27 -8
- package/server/lib/cos-operations-meetings.ts +1 -1
- package/server/lib/domains.ts +2 -2
- package/server/lib/maintenance-lifecycle.ts +1 -1
- package/server/lib/meeting-audio-archive.ts +3 -3
- package/server/lib/meeting-corrections.ts +3 -3
- package/server/lib/meeting-relabel-enrolment.ts +1 -1
- package/server/lib/meeting-relabel.ts +2 -2
- package/server/lib/meeting-scribe-content.ts +7 -7
- package/server/lib/meeting-speaker-review.ts +3 -3
- package/server/lib/meeting-summary.ts +1 -1
- package/server/lib/message-era-reset.ts +1 -1
- package/server/lib/message-era.ts +1 -1
- package/server/lib/morning-brief-config.ts +1 -1
- package/server/lib/morning-brief-coverage.ts +1 -1
- package/server/lib/occupancy-probes.ts +7 -4
- package/server/lib/ollama-tools.ts +1 -1
- package/server/lib/profile.ts +29 -5
- package/server/lib/python-bridge.ts +6 -4
- package/server/lib/query-job-types.ts +1 -1
- package/server/lib/session-stream-events.ts +3 -3
- package/server/lib/session-transcript-watcher.ts +1 -1
- package/server/lib/speaker-calibration-log.ts +1 -1
- package/server/lib/speaker-name.ts +1 -1
- package/server/lib/thread-occupancy.ts +1 -1
- package/server/lib/thread-turn-queue-deliver.ts +1 -1
- package/server/lib/thread-turn-queue.ts +3 -3
- package/server/lib/video-upload-v2.ts +1 -1
- package/server/lib/voice-profile-store.ts +2 -2
- package/server/lib/whisper-metal-gate.ts +1 -1
- package/server/routes/agent-session-bindings.ts +2 -2
- package/server/routes/agent-session-stream.ts +1 -1
- package/server/routes/agent-sessions.ts +2 -2
- package/server/routes/claude-sessions.ts +1 -1
- package/server/routes/health.ts +3 -1
- package/server/routes/meeting.ts +8 -8
- package/server/routes/memory.ts +66 -7
- package/server/routes/prompt-drafts.ts +1 -1
- package/server/routes/session-index.ts +1 -1
- package/server/routes/thread-turn-queue.ts +10 -4
- package/server/routes/transcribe-stream.ts +15 -4
- package/server/routes/voice.ts +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,35 @@
|
|
|
1
|
+
## 6.45.1
|
|
2
|
+
|
|
3
|
+
Knowledge opens with a bounded graph and supports verified question plans for COS Control 0.5.208.
|
|
4
|
+
|
|
5
|
+
- Expose the initial overview through the authenticated workspace route. Model-assisted graph answers may carry validated anchors, waypoints and traversal results from the configured advanced pipeline. Plans navigate only; they cannot save or activate knowledge.
|
|
6
|
+
- Add paired owner-name setup and editing with conflict detection, durable private profile writes and preservation of existing vocabulary and settings. Display identity does not transfer journal or indexing authority.
|
|
7
|
+
- Report whether retained captures have a saveable transcript, unfinished transcription, or completed audio with no usable speech. Empty session shells are not advertised as recoverable recordings. Audio and late-upload admission retain their existing protections.
|
|
8
|
+
|
|
9
|
+
## 6.45.0
|
|
10
|
+
|
|
11
|
+
Versioned local memory and explicit session identity for COS Control 0.5.207.
|
|
12
|
+
|
|
13
|
+
- Ship the standalone memory runtime with owner-scoped journal records,
|
|
14
|
+
revision checks, idempotent operations, deletion frontiers, source provenance,
|
|
15
|
+
review history, and bounded relationship paths with identity constraints.
|
|
16
|
+
- Expose memory workspace capabilities and installation diagnostics. Optional
|
|
17
|
+
private graph SDK integration remains in the configured local harness.
|
|
18
|
+
- Resolve Claude Desktop aliases using explicit CLI identifiers, preserve real
|
|
19
|
+
forks with identical titles, and reject ambiguous or missing transcript targets.
|
|
20
|
+
- Allow bounded descriptor scans on busy Macs while keeping attachment refusals
|
|
21
|
+
conservative. Reuse a refusal within one queue pass; check again before delivery.
|
|
22
|
+
- Learning evidence distinguishes retrieval from application and observed
|
|
23
|
+
outcomes. The release does not claim measured long-term error reduction.
|
|
24
|
+
- Refresh the tested query-parser dependency to qs 6.16.0, closing the
|
|
25
|
+
production dependency audit findings for array limits and isBuffer handling.
|
|
26
|
+
|
|
1
27
|
## 6.44.16
|
|
2
28
|
|
|
3
29
|
A G2 recording keeps its session id when its sidecar only exists in the server's own recordings folder.
|
|
4
30
|
|
|
5
|
-
-
|
|
6
|
-
recent meetings have no session id." On
|
|
31
|
+
- Casey and Sam, 2026-09-08: "Meetings to review is empty, and it says 3
|
|
32
|
+
recent meetings have no session id." On the user's Mac the same three-row case
|
|
7
33
|
was three KC-week recordings whose markdown reached the operations tree
|
|
8
34
|
through git while the `.g2-chunks.json` sidecar, which is gitignored there,
|
|
9
35
|
did not; the row lost its session id and Speakers could not open it.
|
|
@@ -18,8 +44,8 @@ A G2 recording keeps its session id when its sidecar only exists in the server's
|
|
|
18
44
|
|
|
19
45
|
Voice profiles named after a spoken sentence are repaired at load.
|
|
20
46
|
|
|
21
|
-
-
|
|
22
|
-
was the entire enrolment speech ("My Voice My Name Is
|
|
47
|
+
- Casey 2026-09-08: her `voice-profiles.json` held two profiles whose name
|
|
48
|
+
was the entire enrolment speech ("My Voice My Name Is Casey Example And I
|
|
23
49
|
Am The Director…", ~600 characters), written by a client older than the
|
|
24
50
|
8/25 guards. Speaker ID was "active" and could never match anything, and the
|
|
25
51
|
file cannot be repaired by hand because the server rewrites it from memory.
|
|
@@ -38,17 +64,17 @@ Voice profiles named after a spoken sentence are repaired at load.
|
|
|
38
64
|
The Manage sheet's merge, with a preview, a worker and a receipt, plus a
|
|
39
65
|
duplicates scan that proposes and never merges.
|
|
40
66
|
|
|
41
|
-
-
|
|
42
|
-
"address any of the obvious duplicates like the
|
|
43
|
-
from above without clobbering entities. You did good in deflecting
|
|
44
|
-
mallard vs the other
|
|
67
|
+
- the user 2026-09-08: "Build the Manage merge path with the preview", and
|
|
68
|
+
"address any of the obvious duplicates like the alxe and sam example
|
|
69
|
+
from above without clobbering entities. You did good in deflecting alex
|
|
70
|
+
mallard vs the other alex profiles."
|
|
45
71
|
- `POST /api/context/graph/merge/preview` `{ source, target }`: both entities
|
|
46
72
|
from this Mac's index, shared neighbors, the effect (relationships moved and
|
|
47
73
|
collapsed, texts re-embedded, about how long), a name signal, warnings
|
|
48
74
|
(types differ, larger into smaller, nothing links them, a large merge), and
|
|
49
75
|
`blocked` with its reason for a pair the graph knows to be different people
|
|
50
|
-
(
|
|
51
|
-
|
|
76
|
+
(Alex Example / Alex Other, Jordan Example / Jordan Other, the Alexes, the
|
|
77
|
+
Jordans, and any pair the user kept apart).
|
|
52
78
|
- `POST /api/context/graph/merge` `{ source, target, confirm: true, rule? }`:
|
|
53
79
|
starts ONE detached worker on the ingestion owner and answers 202 with a
|
|
54
80
|
ticket and the receipt. The worker takes the exclusive ingest lock, copies
|
|
@@ -66,7 +92,7 @@ duplicates scan that proposes and never merges.
|
|
|
66
92
|
whole names within two letters, one name spelling out the other, a bare
|
|
67
93
|
first name that matches exactly one full name), grouped under the full name
|
|
68
94
|
with the most connections, with shared-neighbor counts and a confidence.
|
|
69
|
-
Possessives ("
|
|
95
|
+
Possessives ("Alex Example's Son") and ambiguous first names are left alone;
|
|
70
96
|
blocked pairs never share a group. Nothing here merges.
|
|
71
97
|
- Four bridge commands: `graph-merge-preview`, `graph-merge`,
|
|
72
98
|
`graph-merge-status`, `graph-duplicates` (28 in the parity set).
|
|
@@ -78,7 +104,7 @@ duplicates scan that proposes and never merges.
|
|
|
78
104
|
|
|
79
105
|
Prune or accept a captured memory, and guardrails that prune nonsense for you.
|
|
80
106
|
|
|
81
|
-
-
|
|
107
|
+
- the user 2026-09-07, looking at a captured decision that read "AAAA...":
|
|
82
108
|
"we need the ability to either prune or accept the memory", and "an
|
|
83
109
|
automated review ... another person could set their own guardrails that
|
|
84
110
|
automatically just prune out stuff like this."
|
|
@@ -101,7 +127,7 @@ Prune or accept a captured memory, and guardrails that prune nonsense for you.
|
|
|
101
127
|
|
|
102
128
|
The Knowledge down-select, and a fail-closed embedding gate.
|
|
103
129
|
|
|
104
|
-
-
|
|
130
|
+
- the user 2026-09-07: the local embeddings are the free path for people who
|
|
105
131
|
run COS without an OpenAI key, and the setup's job is to help each person
|
|
106
132
|
down-select. The `embedding` block now carries `preference.local_only`
|
|
107
133
|
(the one question the user answers: may text leave this Mac?),
|
|
@@ -474,7 +500,7 @@ learned the hard way: a source that cannot be read produces one honest
|
|
|
474
500
|
decision, a date, a dollar figure, an owner) make the cut; the scribe's
|
|
475
501
|
extracted action-item list is never pasted verbatim. Enable the "Workspace
|
|
476
502
|
skill" source with `/good-morning` and that skill's output IS the brief —
|
|
477
|
-
which is how
|
|
503
|
+
which is how the user's own routine rides this without being rewritten.
|
|
478
504
|
|
|
479
505
|
Surface:
|
|
480
506
|
|
|
@@ -970,7 +996,7 @@ topics, decisions and action items. Until now they got none of that, and the
|
|
|
970
996
|
server compensated by returning the TRANSCRIPT in the summary slot -- which is
|
|
971
997
|
why this was reported as "no proper summary and transcription."
|
|
972
998
|
|
|
973
|
-
FEATURE DEFAULTS NOW ON (
|
|
999
|
+
FEATURE DEFAULTS NOW ON (the user 2026-08-25)
|
|
974
1000
|
|
|
975
1001
|
Ship the capability active; users opt out of what they do not need. Each gate
|
|
976
1002
|
stays strict in the other direction -- only a literal '0' disables, so a stray
|
|
@@ -1321,7 +1347,7 @@ the assertion written for them.
|
|
|
1321
1347
|
COS Control's "Show Claude sessions" checkbox sourced its state from
|
|
1322
1348
|
`GET /api/claude-sessions` -- the call that also lists every session -- and the
|
|
1323
1349
|
panel never made that call. The box rendered false while the setting was true, and
|
|
1324
|
-
|
|
1350
|
+
the user enabled it four times against a control that could only ever show him one
|
|
1325
1351
|
value. Health is what every other toggle on that panel already reads, and this is
|
|
1326
1352
|
a pure env read, so it costs nothing on a poll.
|
|
1327
1353
|
|
|
@@ -1761,7 +1787,7 @@ bring the leftover cards back.
|
|
|
1761
1787
|
|
|
1762
1788
|
|
|
1763
1789
|
## 6.36.7
|
|
1764
|
-
- **A turn spoken at a busy thread is now queued instead of refused.**
|
|
1790
|
+
- **A turn spoken at a busy thread is now queued instead of refused.** the user: "if
|
|
1765
1791
|
there's a session that's still running, that would just put it into the queue the same
|
|
1766
1792
|
way that the user has the ability to do so." The thread was never stuck — measured at
|
|
1767
1793
|
the moment he asked, its transcript mtime was 3s old against a 30s window, so the gate
|
|
@@ -1778,7 +1804,7 @@ bring the leftover cards back.
|
|
|
1778
1804
|
attach and turn routes. Those carry the gate, the target fence, the per-target claim,
|
|
1779
1805
|
the watermark, the idempotency ledger and the child-pid accounting; a second copy in a
|
|
1780
1806
|
background worker would be a second place for the gate to drift.
|
|
1781
|
-
- **The watermark exemption, approved explicitly by
|
|
1807
|
+
- **The watermark exemption, approved explicitly by the user.** A queued turn drains after
|
|
1782
1808
|
the thread has moved on — that is what it waited for — so its binding is minted fresh
|
|
1783
1809
|
at delivery. Checked as normal, a queue would fail 100% of the time. An interactive
|
|
1784
1810
|
turn still gets the full divergence check.
|
|
@@ -1793,7 +1819,7 @@ bring the leftover cards back.
|
|
|
1793
1819
|
|
|
1794
1820
|
|
|
1795
1821
|
## 6.36.6
|
|
1796
|
-
- **The session digest follows the thread instead of its opening.**
|
|
1822
|
+
- **The session digest follows the thread instead of its opening.** the user, from the
|
|
1797
1823
|
lens: "It's currently showing a legacy session that I had over a day ago... The
|
|
1798
1824
|
discussion should show the questions that we're asking and a summary of those most
|
|
1799
1825
|
recent things, not something that's the 'first' message." The whole budget now goes
|
|
@@ -2017,7 +2043,7 @@ bring the leftover cards back.
|
|
|
2017
2043
|
- **Session bodies get a real digest.** `GET /api/agent-sessions/:provider/:id` adds
|
|
2018
2044
|
`discussion_digest`: up to **2000 chars** of what actually happened — the opening
|
|
2019
2045
|
ask, the most recent user turns in order, and where the assistant left off.
|
|
2020
|
-
- **The list row is untouched at 180.**
|
|
2046
|
+
- **The list row is untouched at 180.** the user: "it should be in the body not the
|
|
2021
2047
|
title, the row should be no more than the 180 characters." `discussion_summary`
|
|
2022
2048
|
keeps its 180-char budget for the single-line row; the digest is a separate field
|
|
2023
2049
|
the detail page reads. One shared field could not serve both — a 2000-char gist
|
|
@@ -2152,8 +2178,8 @@ bring the leftover cards back.
|
|
|
2152
2178
|
|
|
2153
2179
|
**Measured reach, so callers do not assume more than it delivers.** The distance
|
|
2154
2180
|
budget is 1 edit for 5-8 character words, so it catches single-edit misses
|
|
2155
|
-
(`Austen` → Austin, `Nyala` →
|
|
2156
|
-
`
|
|
2181
|
+
(`Austen` → Austin, `Nyala` → Riley) but NOT `Rileyy` → Riley (2 edits) or
|
|
2182
|
+
`Exampel` → Example (3). Wiring it does **not** remove the need for explicit
|
|
2157
2183
|
`whisper_corrections` entries on multi-edit misses; the new test asserts both
|
|
2158
2184
|
directions so that is not re-derived later.
|
|
2159
2185
|
|
|
@@ -2244,7 +2270,7 @@ four defects, since the writer was correct in every case. 8 mutations, all caugh
|
|
|
2244
2270
|
|
|
2245
2271
|
A video attachment is summarized from stills. The old rule was one still per 15
|
|
2246
2272
|
seconds, floored at 1 and capped at 8, which gave a 12 second clip **one** frame
|
|
2247
|
-
and a 44 second clip **three** - not enough to tell what a video contains.
|
|
2273
|
+
and a 44 second clip **three** - not enough to tell what a video contains. the user,
|
|
2248
2274
|
on a fridge sweep: "it only selects three chunks from the video."
|
|
2249
2275
|
|
|
2250
2276
|
- Frame count is now `clamp(round(seconds / 6), 8, 16)`. A 12s clip and a 44s
|
|
@@ -2354,7 +2380,7 @@ Proof: 460 suites / 1,515 tests, isolated runtime directory, plus clean TypeScri
|
|
|
2354
2380
|
|
|
2355
2381
|
## 6.24.3
|
|
2356
2382
|
|
|
2357
|
-
Auto-recovery of quarantined audio has never run in production.
|
|
2383
|
+
Auto-recovery of quarantined audio has never run in production. the user saw the symptom
|
|
2358
2384
|
for three turns: "1 recoverable" that opening the phone app could not clear.
|
|
2359
2385
|
|
|
2360
2386
|
- **My call sat inside a bare `catch {}`.** `autoRecoverOneQuarantinedCapture()` was one
|
|
@@ -2427,8 +2453,8 @@ sidebar does.
|
|
|
2427
2453
|
- `project` comes from the `cwd` on the session records, not from decoding the
|
|
2428
2454
|
directory name. Claude Code replaces path separators with dashes and a real dash is
|
|
2429
2455
|
indistinguishable from a separator afterwards, so
|
|
2430
|
-
`-Users-
|
|
2431
|
-
"
|
|
2456
|
+
`-Users-example-Documents-GitHub-Example-Chief-Of-Staff-MU-Chief-Staff` decoded to
|
|
2457
|
+
"Example-Chief-Of-Staff-MU-Chief-Staff" where the sidebar says "MU-Chief-Staff".
|
|
2432
2458
|
|
|
2433
2459
|
## 6.24.0
|
|
2434
2460
|
|
|
@@ -2454,7 +2480,7 @@ sessions on this Mac arrives behind a flag.
|
|
|
2454
2480
|
31.7 MB was parsed synchronously per request, the detail endpoint included, on a
|
|
2455
2481
|
process that also streams live audio; now async and cached against file identity
|
|
2456
2482
|
(161ms cold, 15ms warm). And the filename filter matched iCloud sync-conflict
|
|
2457
|
-
duplicates: `.
|
|
2483
|
+
duplicates: `.session_index_cache_Example-Mac-Studio 3.json` shared 543 of its 602
|
|
2458
2484
|
rows with the canonical file, so the merged list served 543 duplicates. Verified on
|
|
2459
2485
|
real data: 37,700 naive becomes 37,157 served, exactly 543 removed. Filtering ` N`
|
|
2460
2486
|
filenames would have been wrong in the other direction, since ` 2.json` holds 245
|
|
@@ -2554,7 +2580,7 @@ hardcoded. 14 new tests here, full suite 1425 serially.
|
|
|
2554
2580
|
|
|
2555
2581
|
## 6.23.0
|
|
2556
2582
|
|
|
2557
|
-
A recording whose phone goes away now becomes a meeting on its own.
|
|
2583
|
+
A recording whose phone goes away now becomes a meeting on its own. the user: "we end
|
|
2558
2584
|
up with a meeting that is orphaned that we have no ability to keep."
|
|
2559
2585
|
|
|
2560
2586
|
Found live while writing this: two sessions stranded for 184 and 24 minutes, holding
|
|
@@ -2609,7 +2635,7 @@ flake under file parallelism, which is a pre-existing isolation bug.
|
|
|
2609
2635
|
## 6.22.1
|
|
2610
2636
|
|
|
2611
2637
|
Notes attached from somewhere else by a symlink are now read properly. Found by
|
|
2612
|
-
|
|
2638
|
+
Sam within hours of 6.22.0, on the very first real setup.
|
|
2613
2639
|
|
|
2614
2640
|
- **A symlinked subfolder or file was silently skipped.** `readdirSync` reports a
|
|
2615
2641
|
symlink as `isSymbolicLink()`, never as `isDirectory()` or `isFile()`, so the walk
|
|
@@ -2846,15 +2872,15 @@ nothing she could name would work.
|
|
|
2846
2872
|
correction row has recorded `proseStale: true` for exactly this since the feature
|
|
2847
2873
|
shipped — and NOTHING read it.
|
|
2848
2874
|
|
|
2849
|
-
|
|
2850
|
-
only him and
|
|
2851
|
-
sites were rewritten correctly. The LLM summary still opened "
|
|
2852
|
-
|
|
2875
|
+
Regression example (synthetic names): the user removed "Morgan Example" from a personal call that was
|
|
2876
|
+
only him and Sam (a third participant's voice matched a similar profile). All 8 label
|
|
2877
|
+
sites were rewritten correctly. The LLM summary still opened "the user, Sam, and
|
|
2878
|
+
Morgan talk through..." and the payload said nothing at all. The allowlist covered
|
|
2853
2879
|
it only as "not confirmed", which is far too weak: he did not fail to confirm
|
|
2854
2880
|
that person, he explicitly said they were not in the room.
|
|
2855
2881
|
|
|
2856
2882
|
The payload now carries `removedNames` and states it above the write-up: *"You
|
|
2857
|
-
removed "
|
|
2883
|
+
removed "Morgan Example" from this meeting. The write-up below was written before
|
|
2858
2884
|
that and still uses the name: treat every mention of it as a capture error, not
|
|
2859
2885
|
a participant."* The prose is left intact — the record stays, the correction
|
|
2860
2886
|
travels beside it.
|
|
@@ -2897,7 +2923,7 @@ without the floor.
|
|
|
2897
2923
|
person: `"MU" by wearing the device; "Gina Obert" by voice match; "Luke Henry"
|
|
2898
2924
|
because a human named that voice`. Labels are quoted, so a typed "Smith, John"
|
|
2899
2925
|
cannot read as two people, and the caveat now covers different SPELLINGS of a
|
|
2900
|
-
confirmed name (labels are `MU` while the prose says "
|
|
2926
|
+
confirmed name (labels are `MU` while the prose says "the user").
|
|
2901
2927
|
|
|
2902
2928
|
- **A lone named voice no longer gets a share.** "100% of identified speech" is
|
|
2903
2929
|
always true with one name and reads as "he did all the talking" — on 23 real
|
|
@@ -3135,7 +3161,7 @@ without the floor.
|
|
|
3135
3161
|
- Review playback falls back to ext-audio. The 7-day archive introduced in
|
|
3136
3162
|
6.21.18 is FORWARD-ONLY — it starts filling when a meeting is saved under that
|
|
3137
3163
|
version — so on upgrade day the panel had no play buttons at all, which is what
|
|
3138
|
-
|
|
3164
|
+
the user hit. ext-audio already holds 72 hours of unrecognised-speaker audio keyed
|
|
3139
3165
|
by the same raw capture index; measured across 14 real meetings, 90-100% of
|
|
3140
3166
|
those files correspond to a chunk the sidecar labels `Ext`. That is exactly the
|
|
3141
3167
|
set a reviewer most needs to hear.
|
|
@@ -3149,7 +3175,7 @@ without the floor.
|
|
|
3149
3175
|
## 6.21.18
|
|
3150
3176
|
|
|
3151
3177
|
Everything a human needs to correct who spoke, and to hear the voice before
|
|
3152
|
-
deciding. Four changes that landed together after
|
|
3178
|
+
deciding. Four changes that landed together after the user reviewed the 2026-08-06
|
|
3153
3179
|
Ditto meeting and found eleven attributed voices, most of them wrong.
|
|
3154
3180
|
|
|
3155
3181
|
- A NAME MUST BE EARNED. `identifySpeaker` accepts a match at 0.55, so one
|
|
@@ -3170,7 +3196,7 @@ Ditto meeting and found eleven attributed voices, most of them wrong.
|
|
|
3170
3196
|
discarding which meeting each sample came from, and now stamps
|
|
3171
3197
|
`g2-training:<sessionId>`. Samples written before this are relabellable but not
|
|
3172
3198
|
retractable.
|
|
3173
|
-
- MEETING AUDIO KEPT 7 DAYS, 8 GB budget (
|
|
3199
|
+
- MEETING AUDIO KEPT 7 DAYS, 8 GB budget (the user's call: review should survive to
|
|
3174
3200
|
a weekend). Chunk WAVs previously died with the batch pipeline —
|
|
3175
3201
|
`session-audio` held 0 files. Hard-linked at the single choke point before the
|
|
3176
3202
|
rename, so it costs no extra disk and outlives the pipeline's cleanup. Sized on
|
|
@@ -3201,7 +3227,7 @@ FIXES FOUND BY QA BEFORE PUBLISH (nothing above ever shipped):
|
|
|
3201
3227
|
read "Unidentified voice" in 4 of 9 meetings, once with 285 of their own
|
|
3202
3228
|
segments. `thrashesWith` still renders, so a mixed row is still visible.
|
|
3203
3229
|
- DE-ATTRIBUTION NUMBERS ITS LABELS. `Unidentified 1`, `Unidentified 2`, … rather
|
|
3204
|
-
than one shared `Ext`.
|
|
3230
|
+
than one shared `Ext`. the user found five wrong attributions in one meeting;
|
|
3205
3231
|
collapsing them into a single row would have destroyed his ability to tell
|
|
3206
3232
|
those voices apart, which is exactly what playback is for next.
|
|
3207
3233
|
- DE-ATTRIBUTION DELETES THE ATTENDEE BULLET instead of renaming it. Renaming
|
|
@@ -3256,7 +3282,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
|
|
|
3256
3282
|
|
|
3257
3283
|
- `POST /api/meeting/:sessionId/relabel` — correct who a voice was in ONE
|
|
3258
3284
|
meeting. Body `{ from, to, chunks?, confirm | dryRun, force? }`.
|
|
3259
|
-
- Per-meeting by design, not a global merge.
|
|
3285
|
+
- Per-meeting by design, not a global merge. the user: "changing it doesn't mean
|
|
3260
3286
|
that all previous chunks should also be moved. It should be meeting by
|
|
3261
3287
|
meeting, with the goal of hardening or refining the voice profiles." The
|
|
3262
3288
|
identifier mishearing a voice in one room is not evidence that every past
|
|
@@ -3276,7 +3302,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
|
|
|
3276
3302
|
human never selected. The response says why rather than staying silent.
|
|
3277
3303
|
- Narrative prose is NEVER rewritten — only reported as `proseStale` with the
|
|
3278
3304
|
forms found. Verified on a real scribe: 6 of 12 speakers are referred to by
|
|
3279
|
-
bare first name in the summary, and with two
|
|
3305
|
+
bare first name in the summary, and with two Alexes, two Jordans and two
|
|
3280
3306
|
Chrises in this org a first-name substitution would rewrite sentences about
|
|
3281
3307
|
someone else. The confirmation message says this before a human commits.
|
|
3282
3308
|
- `/api/health` reports `speaker_corrections` (sessions, applied, pending,
|
|
@@ -3296,7 +3322,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
|
|
|
3296
3322
|
- `Ext` is deliberately included. An unidentified voice is the one case with no
|
|
3297
3323
|
other route to being trained at all.
|
|
3298
3324
|
- RETENTION IS 14 DAYS, not the 8 hours used for audio snippets
|
|
3299
|
-
(`COS_CHUNK_EMBEDDING_TTL_DAYS` to change it). Two reasons, both worth
|
|
3325
|
+
(`COS_CHUNK_EMBEDDING_TTL_DAYS` to change it). Two reasons, both worth the user
|
|
3300
3326
|
overriding if he disagrees: 8 hours cannot survive a weekend, so a Friday
|
|
3301
3327
|
meeting could never be corrected on Monday; and an embedding is not audio — it
|
|
3302
3328
|
is a non-invertible timbre vector that cannot be played back, so the privacy
|
|
@@ -3596,7 +3622,7 @@ unsaved capture, and makes batch status stop lying about finished work.
|
|
|
3596
3622
|
- **Unsaved captures are visible.** `/api/health` gains `unsaved_captures`
|
|
3597
3623
|
(count + compact items, same exposure level as `meeting_sync`). The
|
|
3598
3624
|
authenticated `GET /api/meeting/orphans` returns full detail.
|
|
3599
|
-
- **
|
|
3625
|
+
- **the user-triggered recovery, surface-only by decision (2026-08-02).**
|
|
3600
3626
|
`POST /api/meeting/orphans/:sessionId/recover` batch-transcribes the
|
|
3601
3627
|
quarantined WAVs (same segment/enhance/Metal-preempt contract as HQ polish,
|
|
3602
3628
|
under a new `orphan_recovery` maintenance lease), writes a durable scribe,
|
|
@@ -4066,9 +4092,9 @@ unsaved capture, and makes batch status stop lying about finished work.
|
|
|
4066
4092
|
|
|
4067
4093
|
### A cloned Codex conversation gets its own identity
|
|
4068
4094
|
|
|
4069
|
-
|
|
4095
|
+
the user forked "Example project 2.0 build" into "Example project 3.0 build" and the fork never
|
|
4070
4096
|
appeared in the sessions list. It had not failed to index -- it was indexed AS ITS
|
|
4071
|
-
PARENT. The list showed one `
|
|
4097
|
+
PARENT. The list showed one `Example project 2.0 build` row whose modified time was the
|
|
4072
4098
|
fork's activity: two conversations, one identity, and the newer one invisible.
|
|
4073
4099
|
|
|
4074
4100
|
Cloning copies the parent's `session_meta` record wholesale, so the new rollout carries
|
|
@@ -4094,7 +4120,7 @@ and a clone of a clone doubles again.
|
|
|
4094
4120
|
|
|
4095
4121
|
### The seeded query was being crowded out by the steps
|
|
4096
4122
|
|
|
4097
|
-
Caught by probing the live stream after shipping 6.36.2, before
|
|
4123
|
+
Caught by probing the live stream after shipping 6.36.2, before the user tested it: a
|
|
4098
4124
|
real session seeded 8 events -- 6 tool calls, one prose, one status -- and **no
|
|
4099
4125
|
prompt**. The seed took "the last 7 events of any kind", and in a busy run the
|
|
4100
4126
|
user's question is twenty or thirty steps back, so the activity you opened the page
|
|
@@ -4111,12 +4137,12 @@ scrolling window.
|
|
|
4111
4137
|
|
|
4112
4138
|
### The live view stops being a blank slate
|
|
4113
4139
|
|
|
4114
|
-
Three changes, all from
|
|
4140
|
+
Three changes, all from the user watching a real session on hardware.
|
|
4115
4141
|
|
|
4116
4142
|
- **The user's query is now an event.** A `user` record used to be dropped whole,
|
|
4117
4143
|
on the reasoning that "the prompt came from this device" -- true of a Continue
|
|
4118
4144
|
turn and false of the case that matters most, a session running in a Mac window
|
|
4119
|
-
where that record is the question
|
|
4145
|
+
where that record is the question the user typed there and the glasses have never
|
|
4120
4146
|
seen it. Dropping it is why the lens said WORKING and gave no clue what it was
|
|
4121
4147
|
working ON. Tool results stay dropped; harness wrappers
|
|
4122
4148
|
(`<system-reminder>`, `<cos-alarms>`, the memory and bulletin blocks) are
|
|
@@ -4145,7 +4171,7 @@ Needs COS Glasses 6.8.374 to render any of it.
|
|
|
4145
4171
|
|
|
4146
4172
|
### A reply keeps its line structure
|
|
4147
4173
|
|
|
4148
|
-
|
|
4174
|
+
the user, from a G2 screenshot: a session reply arrived on the lens as one unbroken
|
|
4149
4175
|
paragraph carrying three headings and six bullets, none of them visible.
|
|
4150
4176
|
|
|
4151
4177
|
`proseBody` collapsed ALL whitespace, and both the one-line list gist and the
|
|
@@ -4213,7 +4239,7 @@ Needs COS Glasses 6.8.372 to be visible. An older app never calls the route.
|
|
|
4213
4239
|
|
|
4214
4240
|
### The newest assistant reply arrives whole instead of at 160 characters
|
|
4215
4241
|
|
|
4216
|
-
- **The defect.**
|
|
4242
|
+
- **The defect.** the user opened a session on the glasses and the newest reply was cut
|
|
4217
4243
|
off mid-sentence. The session detail payload had NO full-text field: it carried
|
|
4218
4244
|
`discussion_summary` (180 chars) and `discussion_digest` (2000), and the newest
|
|
4219
4245
|
reply appeared only as the `Latest:` line inside the digest, produced by
|
|
@@ -4283,7 +4309,7 @@ Needs COS Glasses 6.8.372 to be visible. An older app never calls the route.
|
|
|
4283
4309
|
### A continued turn now runs with the session's own permissions
|
|
4284
4310
|
|
|
4285
4311
|
- **This widens what Continue can do.** A prompt spoken into the glasses can run
|
|
4286
|
-
tools on the Mac with nobody at the keyboard. Authorized explicitly by
|
|
4312
|
+
tools on the Mac with nobody at the keyboard. Authorized explicitly by the user
|
|
4287
4313
|
after his first real continued turn came back reporting that every tool was
|
|
4288
4314
|
disabled, which is not the point of the feature.
|
|
4289
4315
|
- Claude drops `--permission-mode plan` and the empty `--tools`/`--allowedTools`
|
package/README.md
CHANGED
|
@@ -520,3 +520,12 @@ BIND_HOST=0.0.0.0 npm run start:server
|
|
|
520
520
|
## License
|
|
521
521
|
|
|
522
522
|
MIT. Learn more at [gotcos.com](https://www.gotcos.com).
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
### Optional owner-local memory runtime
|
|
526
|
+
|
|
527
|
+
The package includes a checksummed public Python runtime. It uses a unique owner, separate data root, reviewed memory states, current source snapshots, explicit document-link graph paths and saved explorations. Initial memory search is keyword based; semantic embeddings and inferred extraction are separate optional integrations. No private COS checkout or corpus is included. Existing plain-file setup remains available.
|
|
528
|
+
|
|
529
|
+
Run `glasses-memory-setup --runtime-dir /absolute/path/to/cos-memory-runtime --data-root /absolute/path/to/private-memory` with Python 3.11+ installed. Set `COS_SCRIPTS_DIR` to the runtime directory in your server configuration, then restart the server. Add an explicitly granted Markdown/text source using `venv/bin/python3 manage.py grant /path/to/notes.md --title "Project"` from that runtime directory. The bundled README documents captures, reviews, document links, source refresh, upgrade and staged restore. Keep the latest independent deletion checkpoint separately from restorable snapshots.
|
|
530
|
+
|
|
531
|
+
This is a local owner instance, separate from app.gotcos.com tenants and hosted Sessions. Installing the package does not upload your corpus or enable household sharing. A source build or local setup test is not proof of a published registry version; use the release ledger for actual availability.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
const { resolve, dirname } = require('node:path')
|
|
4
|
+
const { readFileSync } = require('node:fs')
|
|
5
|
+
const { createHash } = require('node:crypto')
|
|
6
|
+
const { spawnSync } = require('node:child_process')
|
|
7
|
+
const args = process.argv.slice(2)
|
|
8
|
+
function option(name) { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : null }
|
|
9
|
+
const runtime = option('--runtime-dir'), data = option('--data-root')
|
|
10
|
+
if (!runtime || !data || args.some((x, i) => i % 2 === 0 && !['--runtime-dir', '--data-root', '--python'].includes(x))) {
|
|
11
|
+
console.error('Usage: glasses-memory-setup --runtime-dir /path/to/runtime --data-root /path/to/private-data [--python python3]')
|
|
12
|
+
process.exit(2)
|
|
13
|
+
}
|
|
14
|
+
const root = resolve(__dirname, '..')
|
|
15
|
+
const manifest = JSON.parse(readFileSync(resolve(root, 'memory-runtime/runtime.json'), 'utf8'))
|
|
16
|
+
if (manifest.protocol !== 1 || !/^[a-zA-Z0-9.-]+\.tar\.gz$/.test(manifest.file)) throw new Error('Invalid runtime manifest')
|
|
17
|
+
const archive = resolve(root, 'memory-runtime', manifest.file)
|
|
18
|
+
if (createHash('sha256').update(readFileSync(archive)).digest('hex') !== manifest.sha256) throw new Error('Runtime bundle checksum mismatch')
|
|
19
|
+
const program = String.raw`
|
|
20
|
+
import json,os,shutil,sys,tarfile,tempfile
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
archive,destination,data=map(Path,sys.argv[1:])
|
|
23
|
+
if sys.version_info<(3,11):raise SystemExit('Python 3.11 or newer is required')
|
|
24
|
+
if destination.resolve()==data.resolve() or destination.resolve() in data.resolve().parents or data.resolve() in destination.resolve().parents:raise SystemExit('Keep runtime code and private data in separate directories')
|
|
25
|
+
destination.mkdir(parents=True,exist_ok=True)
|
|
26
|
+
with tempfile.TemporaryDirectory(prefix='cos-runtime-install-',dir=destination.parent) as staging:
|
|
27
|
+
with tarfile.open(archive,'r:gz') as bundle:
|
|
28
|
+
members=bundle.getmembers()
|
|
29
|
+
if sum(item.size for item in members)>10*1024*1024:raise SystemExit('Runtime bundle is too large')
|
|
30
|
+
for item in members:
|
|
31
|
+
relative=Path(item.name)
|
|
32
|
+
if relative.is_absolute() or '..' in relative.parts or not (item.isfile() or item.isdir()):raise SystemExit('Unsafe runtime member')
|
|
33
|
+
target=Path(staging)/relative
|
|
34
|
+
if item.isdir():target.mkdir(parents=True,exist_ok=True)
|
|
35
|
+
else:
|
|
36
|
+
target.parent.mkdir(parents=True,exist_ok=True)
|
|
37
|
+
with bundle.extractfile(item) as source,target.open('wb') as output:shutil.copyfileobj(source,output)
|
|
38
|
+
roots=list(Path(staging).iterdir())
|
|
39
|
+
if len(roots)!=1 or not roots[0].is_dir():raise SystemExit('Invalid runtime layout')
|
|
40
|
+
for source in roots[0].iterdir():
|
|
41
|
+
if source.name in {'instance.json','venv'} or not source.is_file():raise SystemExit('Unexpected runtime member')
|
|
42
|
+
target=destination/source.name
|
|
43
|
+
if target.is_symlink():raise SystemExit('Refusing to replace symlinked runtime code')
|
|
44
|
+
fd,temp=tempfile.mkstemp(prefix='.'+target.name+'-',dir=destination)
|
|
45
|
+
try:
|
|
46
|
+
with os.fdopen(fd,'wb') as output,source.open('rb') as input:shutil.copyfileobj(input,output);output.flush();os.fsync(output.fileno())
|
|
47
|
+
os.replace(temp,target)
|
|
48
|
+
finally:Path(temp).unlink(missing_ok=True)
|
|
49
|
+
`
|
|
50
|
+
let result = spawnSync(option('--python') || 'python3', ['-c', program, archive, resolve(runtime), resolve(data)], { stdio: 'inherit' })
|
|
51
|
+
if (result.error) throw result.error
|
|
52
|
+
if (result.status !== 0) process.exit(result.status || 1)
|
|
53
|
+
result = spawnSync(option('--python') || 'python3', [resolve(runtime, 'install.py'), '--data-root', resolve(data)], { stdio: 'inherit' })
|
|
54
|
+
if (result.error) throw result.error
|
|
55
|
+
process.exit(result.status || 0)
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotcos/glasses-server",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "COS Glasses
|
|
3
|
+
"version": "6.45.1",
|
|
4
|
+
"description": "COS Glasses — 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",
|
|
8
|
-
"glasses-server-managed": "bin/managed-server.cjs"
|
|
8
|
+
"glasses-server-managed": "bin/managed-server.cjs",
|
|
9
|
+
"glasses-memory-setup": "bin/memory-setup.cjs"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start": "node bin/cli.cjs",
|
|
@@ -41,7 +42,9 @@
|
|
|
41
42
|
"README.md",
|
|
42
43
|
"LICENSE",
|
|
43
44
|
"CHANGELOG.md",
|
|
44
|
-
"SECURITY.md"
|
|
45
|
+
"SECURITY.md",
|
|
46
|
+
"bin/memory-setup.cjs",
|
|
47
|
+
"memory-runtime"
|
|
45
48
|
],
|
|
46
49
|
"author": "COS Contributors",
|
|
47
50
|
"license": "MIT",
|
package/server/index.ts
CHANGED
|
@@ -334,7 +334,7 @@ const attachedWorkspaceDeps = realAttachedWorkspaceDeps(nativeHeadDeps)
|
|
|
334
334
|
* that also registers the write routes (6.33.0).
|
|
335
335
|
*
|
|
336
336
|
* Without the clock, a foreign holder is terminal and Continue is refused for
|
|
337
|
-
* any thread with a Claude Code window open on it — including the ones
|
|
337
|
+
* any thread with a Claude Code window open on it — including the ones the user
|
|
338
338
|
* actually works in, because he leaves those windows open. With it, a holder
|
|
339
339
|
* measured idle is continuable and only a holder measured WRITING is refused.
|
|
340
340
|
*
|
|
@@ -518,7 +518,7 @@ app.use('/api', claudeSessionsRouter)
|
|
|
518
518
|
// QUEUED THREAD TURNS
|
|
519
519
|
// ---------------------------------------------------------------------------
|
|
520
520
|
// A turn spoken at a thread that is busy right now is PARKED instead of refused, and
|
|
521
|
-
// delivered when the thread frees.
|
|
521
|
+
// delivered when the thread frees. the user, 2026-08-17: "if there's a session that's
|
|
522
522
|
// still running, that would just put it into the queue the same way that the user has
|
|
523
523
|
// the ability to do so."
|
|
524
524
|
//
|
|
@@ -550,7 +550,7 @@ if (threadAttachEnabled()) {
|
|
|
550
550
|
// `OccupancyReason` has no `native_target_busy` member, because that refusal is
|
|
551
551
|
// produced only by the binding registry inside `bindings.create`.
|
|
552
552
|
//
|
|
553
|
-
// That gap is what trapped
|
|
553
|
+
// That gap is what trapped the user on 2026-08-18. Continue refused
|
|
554
554
|
// `native_target_busy` (a live COS binding held the thread), the client armed the
|
|
555
555
|
// queue, he dictated -- and this gate, asking occupancy alone, answered
|
|
556
556
|
// `attachable: true`, so the route replied `409 thread_free` ("Pick Continue again
|
|
@@ -623,7 +623,7 @@ app.use('/api', createAgentSessionBindingsRouter({
|
|
|
623
623
|
// without a reachable release is a REGRESSION, not a fix: today "Restart
|
|
624
624
|
// Server" in COS Control clears a fence, and making it survive restarts with
|
|
625
625
|
// no operator surface in Control turns an 8-second annoyance into a
|
|
626
|
-
// permanently dead thread that needs a terminal to clear.
|
|
626
|
+
// permanently dead thread that needs a terminal to clear. the user, 2026-08-12:
|
|
627
627
|
// "we couldn't do anything without bash, that shouldn't be the case."
|
|
628
628
|
//
|
|
629
629
|
// So the storage ships inert. GET /agent-sessions/fences and
|
|
@@ -159,7 +159,7 @@ export function analyzePlaybackWav(buffer: Buffer): AudioSignalProfile | null {
|
|
|
159
159
|
|
|
160
160
|
export function adaptivePlaybackEnabled(): boolean {
|
|
161
161
|
// Private canary: explicit opt-in, explicit 0 rollback through COS Control.
|
|
162
|
-
// Default ON since 6.37.0 (
|
|
162
|
+
// Default ON since 6.37.0 (the user 2026-08-25). Absent key = on; only a
|
|
163
163
|
// literal '0' disables. Raw WAV is untouched either way — this affects
|
|
164
164
|
// review playback only, never the transcript, attribution, save or sync.
|
|
165
165
|
return process.env.COS_MEETING_AUDIO_ADAPTIVE_PLAYBACK !== '0'
|