@gotcos/glasses-server 6.44.15 → 6.45.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +83 -49
  2. package/README.md +9 -0
  3. package/bin/memory-setup.cjs +55 -0
  4. package/memory-runtime/cos-memory-runtime-0.1.0.tar.gz +0 -0
  5. package/memory-runtime/runtime.json +7 -0
  6. package/package.json +6 -3
  7. package/server/index.ts +4 -4
  8. package/server/lib/adaptive-playback-audio.ts +1 -1
  9. package/server/lib/agent-session-store.ts +139 -28
  10. package/server/lib/archive-search.ts +1 -1
  11. package/server/lib/attached-provider-adapter.ts +1 -1
  12. package/server/lib/attached-workspace.ts +3 -3
  13. package/server/lib/chunk-embedding-store.ts +1 -1
  14. package/server/lib/context-files.ts +1 -1
  15. package/server/lib/cos-context-browser.ts +17 -6
  16. package/server/lib/cos-operations-meetings.ts +15 -1
  17. package/server/lib/domains.ts +2 -2
  18. package/server/lib/maintenance-lifecycle.ts +1 -1
  19. package/server/lib/meeting-audio-archive.ts +3 -3
  20. package/server/lib/meeting-corrections.ts +3 -3
  21. package/server/lib/meeting-relabel-enrolment.ts +1 -1
  22. package/server/lib/meeting-relabel.ts +2 -2
  23. package/server/lib/meeting-scribe-content.ts +7 -7
  24. package/server/lib/meeting-speaker-review.ts +3 -3
  25. package/server/lib/meeting-summary.ts +1 -1
  26. package/server/lib/message-era-reset.ts +1 -1
  27. package/server/lib/message-era.ts +1 -1
  28. package/server/lib/morning-brief-config.ts +1 -1
  29. package/server/lib/morning-brief-coverage.ts +1 -1
  30. package/server/lib/occupancy-probes.ts +7 -4
  31. package/server/lib/ollama-tools.ts +1 -1
  32. package/server/lib/python-bridge.ts +6 -4
  33. package/server/lib/query-job-types.ts +1 -1
  34. package/server/lib/session-stream-events.ts +3 -3
  35. package/server/lib/session-transcript-watcher.ts +1 -1
  36. package/server/lib/speaker-calibration-log.ts +1 -1
  37. package/server/lib/speaker-name.ts +1 -1
  38. package/server/lib/thread-occupancy.ts +1 -1
  39. package/server/lib/thread-turn-queue-deliver.ts +1 -1
  40. package/server/lib/thread-turn-queue.ts +3 -3
  41. package/server/lib/video-upload-v2.ts +1 -1
  42. package/server/lib/voice-profile-store.ts +2 -2
  43. package/server/lib/whisper-metal-gate.ts +1 -1
  44. package/server/routes/agent-session-bindings.ts +2 -2
  45. package/server/routes/agent-session-stream.ts +1 -1
  46. package/server/routes/agent-sessions.ts +2 -2
  47. package/server/routes/claude-sessions.ts +1 -1
  48. package/server/routes/health.ts +1 -1
  49. package/server/routes/meeting.ts +8 -8
  50. package/server/routes/memory.ts +45 -7
  51. package/server/routes/prompt-drafts.ts +1 -1
  52. package/server/routes/session-index.ts +1 -1
  53. package/server/routes/thread-turn-queue.ts +10 -4
  54. package/server/routes/transcribe-stream.ts +1 -1
  55. package/server/routes/voice.ts +4 -4
package/CHANGELOG.md CHANGED
@@ -1,9 +1,43 @@
1
+ ## 6.45.0
2
+
3
+ Versioned local memory and explicit session identity for COS Control 0.5.207.
4
+
5
+ - Ship the standalone memory runtime with owner-scoped journal records,
6
+ revision checks, idempotent operations, deletion frontiers, source provenance,
7
+ review history, and bounded relationship paths with identity constraints.
8
+ - Expose memory workspace capabilities and installation diagnostics. Optional
9
+ private graph SDK integration remains in the configured local harness.
10
+ - Resolve Claude Desktop aliases using explicit CLI identifiers, preserve real
11
+ forks with identical titles, and reject ambiguous or missing transcript targets.
12
+ - Allow bounded descriptor scans on busy Macs while keeping attachment refusals
13
+ conservative. Reuse a refusal within one queue pass; check again before delivery.
14
+ - Learning evidence distinguishes retrieval from application and observed
15
+ outcomes. The release does not claim measured long-term error reduction.
16
+ - Refresh the tested query-parser dependency to qs 6.16.0, closing the
17
+ production dependency audit findings for array limits and isBuffer handling.
18
+
19
+ ## 6.44.16
20
+
21
+ A G2 recording keeps its session id when its sidecar only exists in the server's own recordings folder.
22
+
23
+ - Casey and Sam, 2026-09-08: "Meetings to review is empty, and it says 3
24
+ recent meetings have no session id." On the user's Mac the same three-row case
25
+ was three KC-week recordings whose markdown reached the operations tree
26
+ through git while the `.g2-chunks.json` sidecar, which is gitignored there,
27
+ did not; the row lost its session id and Speakers could not open it.
28
+ - `listCosOperationsMeetings` now reads the sidecar from the server's own
29
+ recordings root by the same stem when the copy beside the markdown is
30
+ missing (`recordingsRoot` option, default the data dir). The review lookup
31
+ already resolves a session through the standalone store, so the row opens.
32
+ A meeting with no sidecar anywhere still omits the field rather than
33
+ inventing one.
34
+
1
35
  ## 6.44.15
2
36
 
3
37
  Voice profiles named after a spoken sentence are repaired at load.
4
38
 
5
- - Chelsie 2026-09-08: her `voice-profiles.json` held two profiles whose name
6
- was the entire enrolment speech ("My Voice My Name Is Chelsie Hodgkiss And I
39
+ - Casey 2026-09-08: her `voice-profiles.json` held two profiles whose name
40
+ was the entire enrolment speech ("My Voice My Name Is Casey Example And I
7
41
  Am The Director…", ~600 characters), written by a client older than the
8
42
  8/25 guards. Speaker ID was "active" and could never match anything, and the
9
43
  file cannot be repaired by hand because the server rewrites it from memory.
@@ -22,17 +56,17 @@ Voice profiles named after a spoken sentence are repaired at load.
22
56
  The Manage sheet's merge, with a preview, a worker and a receipt, plus a
23
57
  duplicates scan that proposes and never merges.
24
58
 
25
- - Miles 2026-09-08: "Build the Manage merge path with the preview", and
26
- "address any of the obvious duplicates like the miels and queen example
27
- from above without clobbering entities. You did good in deflecting miles
28
- mallard vs the other miles profiles."
59
+ - the user 2026-09-08: "Build the Manage merge path with the preview", and
60
+ "address any of the obvious duplicates like the alxe and sam example
61
+ from above without clobbering entities. You did good in deflecting alex
62
+ mallard vs the other alex profiles."
29
63
  - `POST /api/context/graph/merge/preview` `{ source, target }`: both entities
30
64
  from this Mac's index, shared neighbors, the effect (relationships moved and
31
65
  collapsed, texts re-embedded, about how long), a name signal, warnings
32
66
  (types differ, larger into smaller, nothing links them, a large merge), and
33
67
  `blocked` with its reason for a pair the graph knows to be different people
34
- (Miles Ukaoma / Miles Mallard, Manoj Bisht / Manoj Kumar, the Kyles, the
35
- Jacobuses, and any pair the user kept apart).
68
+ (Alex Example / Alex Other, Jordan Example / Jordan Other, the Alexes, the
69
+ Jordans, and any pair the user kept apart).
36
70
  - `POST /api/context/graph/merge` `{ source, target, confirm: true, rule? }`:
37
71
  starts ONE detached worker on the ingestion owner and answers 202 with a
38
72
  ticket and the receipt. The worker takes the exclusive ingest lock, copies
@@ -50,7 +84,7 @@ duplicates scan that proposes and never merges.
50
84
  whole names within two letters, one name spelling out the other, a bare
51
85
  first name that matches exactly one full name), grouped under the full name
52
86
  with the most connections, with shared-neighbor counts and a confidence.
53
- Possessives ("Miles Ukaoma's Son") and ambiguous first names are left alone;
87
+ Possessives ("Alex Example's Son") and ambiguous first names are left alone;
54
88
  blocked pairs never share a group. Nothing here merges.
55
89
  - Four bridge commands: `graph-merge-preview`, `graph-merge`,
56
90
  `graph-merge-status`, `graph-duplicates` (28 in the parity set).
@@ -62,7 +96,7 @@ duplicates scan that proposes and never merges.
62
96
 
63
97
  Prune or accept a captured memory, and guardrails that prune nonsense for you.
64
98
 
65
- - Miles 2026-09-07, looking at a captured decision that read "AAAA...":
99
+ - the user 2026-09-07, looking at a captured decision that read "AAAA...":
66
100
  "we need the ability to either prune or accept the memory", and "an
67
101
  automated review ... another person could set their own guardrails that
68
102
  automatically just prune out stuff like this."
@@ -85,7 +119,7 @@ Prune or accept a captured memory, and guardrails that prune nonsense for you.
85
119
 
86
120
  The Knowledge down-select, and a fail-closed embedding gate.
87
121
 
88
- - Miles 2026-09-07: the local embeddings are the free path for people who
122
+ - the user 2026-09-07: the local embeddings are the free path for people who
89
123
  run COS without an OpenAI key, and the setup's job is to help each person
90
124
  down-select. The `embedding` block now carries `preference.local_only`
91
125
  (the one question the user answers: may text leave this Mac?),
@@ -458,7 +492,7 @@ learned the hard way: a source that cannot be read produces one honest
458
492
  decision, a date, a dollar figure, an owner) make the cut; the scribe's
459
493
  extracted action-item list is never pasted verbatim. Enable the "Workspace
460
494
  skill" source with `/good-morning` and that skill's output IS the brief —
461
- which is how Miles's own routine rides this without being rewritten.
495
+ which is how the user's own routine rides this without being rewritten.
462
496
 
463
497
  Surface:
464
498
 
@@ -954,7 +988,7 @@ topics, decisions and action items. Until now they got none of that, and the
954
988
  server compensated by returning the TRANSCRIPT in the summary slot -- which is
955
989
  why this was reported as "no proper summary and transcription."
956
990
 
957
- FEATURE DEFAULTS NOW ON (Miles 2026-08-25)
991
+ FEATURE DEFAULTS NOW ON (the user 2026-08-25)
958
992
 
959
993
  Ship the capability active; users opt out of what they do not need. Each gate
960
994
  stays strict in the other direction -- only a literal '0' disables, so a stray
@@ -1305,7 +1339,7 @@ the assertion written for them.
1305
1339
  COS Control's "Show Claude sessions" checkbox sourced its state from
1306
1340
  `GET /api/claude-sessions` -- the call that also lists every session -- and the
1307
1341
  panel never made that call. The box rendered false while the setting was true, and
1308
- Miles enabled it four times against a control that could only ever show him one
1342
+ the user enabled it four times against a control that could only ever show him one
1309
1343
  value. Health is what every other toggle on that panel already reads, and this is
1310
1344
  a pure env read, so it costs nothing on a poll.
1311
1345
 
@@ -1745,7 +1779,7 @@ bring the leftover cards back.
1745
1779
 
1746
1780
 
1747
1781
  ## 6.36.7
1748
- - **A turn spoken at a busy thread is now queued instead of refused.** Miles: "if
1782
+ - **A turn spoken at a busy thread is now queued instead of refused.** the user: "if
1749
1783
  there's a session that's still running, that would just put it into the queue the same
1750
1784
  way that the user has the ability to do so." The thread was never stuck — measured at
1751
1785
  the moment he asked, its transcript mtime was 3s old against a 30s window, so the gate
@@ -1762,7 +1796,7 @@ bring the leftover cards back.
1762
1796
  attach and turn routes. Those carry the gate, the target fence, the per-target claim,
1763
1797
  the watermark, the idempotency ledger and the child-pid accounting; a second copy in a
1764
1798
  background worker would be a second place for the gate to drift.
1765
- - **The watermark exemption, approved explicitly by Miles.** A queued turn drains after
1799
+ - **The watermark exemption, approved explicitly by the user.** A queued turn drains after
1766
1800
  the thread has moved on — that is what it waited for — so its binding is minted fresh
1767
1801
  at delivery. Checked as normal, a queue would fail 100% of the time. An interactive
1768
1802
  turn still gets the full divergence check.
@@ -1777,7 +1811,7 @@ bring the leftover cards back.
1777
1811
 
1778
1812
 
1779
1813
  ## 6.36.6
1780
- - **The session digest follows the thread instead of its opening.** Miles, from the
1814
+ - **The session digest follows the thread instead of its opening.** the user, from the
1781
1815
  lens: "It's currently showing a legacy session that I had over a day ago... The
1782
1816
  discussion should show the questions that we're asking and a summary of those most
1783
1817
  recent things, not something that's the 'first' message." The whole budget now goes
@@ -2001,7 +2035,7 @@ bring the leftover cards back.
2001
2035
  - **Session bodies get a real digest.** `GET /api/agent-sessions/:provider/:id` adds
2002
2036
  `discussion_digest`: up to **2000 chars** of what actually happened — the opening
2003
2037
  ask, the most recent user turns in order, and where the assistant left off.
2004
- - **The list row is untouched at 180.** Miles: "it should be in the body not the
2038
+ - **The list row is untouched at 180.** the user: "it should be in the body not the
2005
2039
  title, the row should be no more than the 180 characters." `discussion_summary`
2006
2040
  keeps its 180-char budget for the single-line row; the digest is a separate field
2007
2041
  the detail page reads. One shared field could not serve both — a 2000-char gist
@@ -2136,8 +2170,8 @@ bring the leftover cards back.
2136
2170
 
2137
2171
  **Measured reach, so callers do not assume more than it delivers.** The distance
2138
2172
  budget is 1 edit for 5-8 character words, so it catches single-edit misses
2139
- (`Austen` → Austin, `Nyala` → Niala) but NOT `Miyala` → Niala (2 edits) or
2140
- `Yukoma` → Ukaoma (3). Wiring it does **not** remove the need for explicit
2173
+ (`Austen` → Austin, `Nyala` → Riley) but NOT `Rileyy` → Riley (2 edits) or
2174
+ `Exampel` → Example (3). Wiring it does **not** remove the need for explicit
2141
2175
  `whisper_corrections` entries on multi-edit misses; the new test asserts both
2142
2176
  directions so that is not re-derived later.
2143
2177
 
@@ -2228,7 +2262,7 @@ four defects, since the writer was correct in every case. 8 mutations, all caugh
2228
2262
 
2229
2263
  A video attachment is summarized from stills. The old rule was one still per 15
2230
2264
  seconds, floored at 1 and capped at 8, which gave a 12 second clip **one** frame
2231
- and a 44 second clip **three** - not enough to tell what a video contains. Miles,
2265
+ and a 44 second clip **three** - not enough to tell what a video contains. the user,
2232
2266
  on a fridge sweep: "it only selects three chunks from the video."
2233
2267
 
2234
2268
  - Frame count is now `clamp(round(seconds / 6), 8, 16)`. A 12s clip and a 44s
@@ -2338,7 +2372,7 @@ Proof: 460 suites / 1,515 tests, isolated runtime directory, plus clean TypeScri
2338
2372
 
2339
2373
  ## 6.24.3
2340
2374
 
2341
- Auto-recovery of quarantined audio has never run in production. Miles saw the symptom
2375
+ Auto-recovery of quarantined audio has never run in production. the user saw the symptom
2342
2376
  for three turns: "1 recoverable" that opening the phone app could not clear.
2343
2377
 
2344
2378
  - **My call sat inside a bare `catch {}`.** `autoRecoverOneQuarantinedCapture()` was one
@@ -2411,8 +2445,8 @@ sidebar does.
2411
2445
  - `project` comes from the `cwd` on the session records, not from decoding the
2412
2446
  directory name. Claude Code replaces path separators with dashes and a real dash is
2413
2447
  indistinguishable from a separator afterwards, so
2414
- `-Users-ukaoma-Documents-GitHub-Ukaoma-Chief-Of-Staff-MU-Chief-Staff` decoded to
2415
- "Ukaoma-Chief-Of-Staff-MU-Chief-Staff" where the sidebar says "MU-Chief-Staff".
2448
+ `-Users-example-Documents-GitHub-Example-Chief-Of-Staff-MU-Chief-Staff` decoded to
2449
+ "Example-Chief-Of-Staff-MU-Chief-Staff" where the sidebar says "MU-Chief-Staff".
2416
2450
 
2417
2451
  ## 6.24.0
2418
2452
 
@@ -2438,7 +2472,7 @@ sessions on this Mac arrives behind a flag.
2438
2472
  31.7 MB was parsed synchronously per request, the detail endpoint included, on a
2439
2473
  process that also streams live audio; now async and cached against file identity
2440
2474
  (161ms cold, 15ms warm). And the filename filter matched iCloud sync-conflict
2441
- duplicates: `.session_index_cache_Ukaoma-Mac-Studio 3.json` shared 543 of its 602
2475
+ duplicates: `.session_index_cache_Example-Mac-Studio 3.json` shared 543 of its 602
2442
2476
  rows with the canonical file, so the merged list served 543 duplicates. Verified on
2443
2477
  real data: 37,700 naive becomes 37,157 served, exactly 543 removed. Filtering ` N`
2444
2478
  filenames would have been wrong in the other direction, since ` 2.json` holds 245
@@ -2538,7 +2572,7 @@ hardcoded. 14 new tests here, full suite 1425 serially.
2538
2572
 
2539
2573
  ## 6.23.0
2540
2574
 
2541
- A recording whose phone goes away now becomes a meeting on its own. Miles: "we end
2575
+ A recording whose phone goes away now becomes a meeting on its own. the user: "we end
2542
2576
  up with a meeting that is orphaned that we have no ability to keep."
2543
2577
 
2544
2578
  Found live while writing this: two sessions stranded for 184 and 24 minutes, holding
@@ -2593,7 +2627,7 @@ flake under file parallelism, which is a pre-existing isolation bug.
2593
2627
  ## 6.22.1
2594
2628
 
2595
2629
  Notes attached from somewhere else by a symlink are now read properly. Found by
2596
- Queen within hours of 6.22.0, on the very first real setup.
2630
+ Sam within hours of 6.22.0, on the very first real setup.
2597
2631
 
2598
2632
  - **A symlinked subfolder or file was silently skipped.** `readdirSync` reports a
2599
2633
  symlink as `isSymbolicLink()`, never as `isDirectory()` or `isFile()`, so the walk
@@ -2830,15 +2864,15 @@ nothing she could name would work.
2830
2864
  correction row has recorded `proseStale: true` for exactly this since the feature
2831
2865
  shipped — and NOTHING read it.
2832
2866
 
2833
- Real case, 2026-08-07: Miles removed "Clem Ukaoma" from a personal call that was
2834
- only him and Queen (his father's voice matched a similar profile). All 8 label
2835
- sites were rewritten correctly. The LLM summary still opened "Miles, Queen, and
2836
- Clem talk through..." and the payload said nothing at all. The allowlist covered
2867
+ Regression example (synthetic names): the user removed "Morgan Example" from a personal call that was
2868
+ only him and Sam (a third participant's voice matched a similar profile). All 8 label
2869
+ sites were rewritten correctly. The LLM summary still opened "the user, Sam, and
2870
+ Morgan talk through..." and the payload said nothing at all. The allowlist covered
2837
2871
  it only as "not confirmed", which is far too weak: he did not fail to confirm
2838
2872
  that person, he explicitly said they were not in the room.
2839
2873
 
2840
2874
  The payload now carries `removedNames` and states it above the write-up: *"You
2841
- removed "Clem Ukaoma" from this meeting. The write-up below was written before
2875
+ removed "Morgan Example" from this meeting. The write-up below was written before
2842
2876
  that and still uses the name: treat every mention of it as a capture error, not
2843
2877
  a participant."* The prose is left intact — the record stays, the correction
2844
2878
  travels beside it.
@@ -2881,7 +2915,7 @@ without the floor.
2881
2915
  person: `"MU" by wearing the device; "Gina Obert" by voice match; "Luke Henry"
2882
2916
  because a human named that voice`. Labels are quoted, so a typed "Smith, John"
2883
2917
  cannot read as two people, and the caveat now covers different SPELLINGS of a
2884
- confirmed name (labels are `MU` while the prose says "Miles").
2918
+ confirmed name (labels are `MU` while the prose says "the user").
2885
2919
 
2886
2920
  - **A lone named voice no longer gets a share.** "100% of identified speech" is
2887
2921
  always true with one name and reads as "he did all the talking" — on 23 real
@@ -3119,7 +3153,7 @@ without the floor.
3119
3153
  - Review playback falls back to ext-audio. The 7-day archive introduced in
3120
3154
  6.21.18 is FORWARD-ONLY — it starts filling when a meeting is saved under that
3121
3155
  version — so on upgrade day the panel had no play buttons at all, which is what
3122
- Miles hit. ext-audio already holds 72 hours of unrecognised-speaker audio keyed
3156
+ the user hit. ext-audio already holds 72 hours of unrecognised-speaker audio keyed
3123
3157
  by the same raw capture index; measured across 14 real meetings, 90-100% of
3124
3158
  those files correspond to a chunk the sidecar labels `Ext`. That is exactly the
3125
3159
  set a reviewer most needs to hear.
@@ -3133,7 +3167,7 @@ without the floor.
3133
3167
  ## 6.21.18
3134
3168
 
3135
3169
  Everything a human needs to correct who spoke, and to hear the voice before
3136
- deciding. Four changes that landed together after Miles reviewed the 2026-08-06
3170
+ deciding. Four changes that landed together after the user reviewed the 2026-08-06
3137
3171
  Ditto meeting and found eleven attributed voices, most of them wrong.
3138
3172
 
3139
3173
  - A NAME MUST BE EARNED. `identifySpeaker` accepts a match at 0.55, so one
@@ -3154,7 +3188,7 @@ Ditto meeting and found eleven attributed voices, most of them wrong.
3154
3188
  discarding which meeting each sample came from, and now stamps
3155
3189
  `g2-training:<sessionId>`. Samples written before this are relabellable but not
3156
3190
  retractable.
3157
- - MEETING AUDIO KEPT 7 DAYS, 8 GB budget (Miles's call: review should survive to
3191
+ - MEETING AUDIO KEPT 7 DAYS, 8 GB budget (the user's call: review should survive to
3158
3192
  a weekend). Chunk WAVs previously died with the batch pipeline —
3159
3193
  `session-audio` held 0 files. Hard-linked at the single choke point before the
3160
3194
  rename, so it costs no extra disk and outlives the pipeline's cleanup. Sized on
@@ -3185,7 +3219,7 @@ FIXES FOUND BY QA BEFORE PUBLISH (nothing above ever shipped):
3185
3219
  read "Unidentified voice" in 4 of 9 meetings, once with 285 of their own
3186
3220
  segments. `thrashesWith` still renders, so a mixed row is still visible.
3187
3221
  - DE-ATTRIBUTION NUMBERS ITS LABELS. `Unidentified 1`, `Unidentified 2`, … rather
3188
- than one shared `Ext`. Miles found five wrong attributions in one meeting;
3222
+ than one shared `Ext`. the user found five wrong attributions in one meeting;
3189
3223
  collapsing them into a single row would have destroyed his ability to tell
3190
3224
  those voices apart, which is exactly what playback is for next.
3191
3225
  - DE-ATTRIBUTION DELETES THE ATTENDEE BULLET instead of renaming it. Renaming
@@ -3240,7 +3274,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
3240
3274
 
3241
3275
  - `POST /api/meeting/:sessionId/relabel` — correct who a voice was in ONE
3242
3276
  meeting. Body `{ from, to, chunks?, confirm | dryRun, force? }`.
3243
- - Per-meeting by design, not a global merge. Miles: "changing it doesn't mean
3277
+ - Per-meeting by design, not a global merge. the user: "changing it doesn't mean
3244
3278
  that all previous chunks should also be moved. It should be meeting by
3245
3279
  meeting, with the goal of hardening or refining the voice profiles." The
3246
3280
  identifier mishearing a voice in one room is not evidence that every past
@@ -3260,7 +3294,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
3260
3294
  human never selected. The response says why rather than staying silent.
3261
3295
  - Narrative prose is NEVER rewritten — only reported as `proseStale` with the
3262
3296
  forms found. Verified on a real scribe: 6 of 12 speakers are referred to by
3263
- bare first name in the summary, and with two Kyles, two Jacobuses and two
3297
+ bare first name in the summary, and with two Alexes, two Jordans and two
3264
3298
  Chrises in this org a first-name substitution would rewrite sentences about
3265
3299
  someone else. The confirmation message says this before a human commits.
3266
3300
  - `/api/health` reports `speaker_corrections` (sessions, applied, pending,
@@ -3280,7 +3314,7 @@ Requires COS Control 0.5.0+ to use the scoped correction and playback surfaces.
3280
3314
  - `Ext` is deliberately included. An unidentified voice is the one case with no
3281
3315
  other route to being trained at all.
3282
3316
  - RETENTION IS 14 DAYS, not the 8 hours used for audio snippets
3283
- (`COS_CHUNK_EMBEDDING_TTL_DAYS` to change it). Two reasons, both worth Miles
3317
+ (`COS_CHUNK_EMBEDDING_TTL_DAYS` to change it). Two reasons, both worth the user
3284
3318
  overriding if he disagrees: 8 hours cannot survive a weekend, so a Friday
3285
3319
  meeting could never be corrected on Monday; and an embedding is not audio — it
3286
3320
  is a non-invertible timbre vector that cannot be played back, so the privacy
@@ -3580,7 +3614,7 @@ unsaved capture, and makes batch status stop lying about finished work.
3580
3614
  - **Unsaved captures are visible.** `/api/health` gains `unsaved_captures`
3581
3615
  (count + compact items, same exposure level as `meeting_sync`). The
3582
3616
  authenticated `GET /api/meeting/orphans` returns full detail.
3583
- - **Miles-triggered recovery, surface-only by decision (2026-08-02).**
3617
+ - **the user-triggered recovery, surface-only by decision (2026-08-02).**
3584
3618
  `POST /api/meeting/orphans/:sessionId/recover` batch-transcribes the
3585
3619
  quarantined WAVs (same segment/enhance/Metal-preempt contract as HQ polish,
3586
3620
  under a new `orphan_recovery` maintenance lease), writes a durable scribe,
@@ -4050,9 +4084,9 @@ unsaved capture, and makes batch status stop lying about finished work.
4050
4084
 
4051
4085
  ### A cloned Codex conversation gets its own identity
4052
4086
 
4053
- Miles forked "Markt POS 2.0 build" into "POS Nation 3.0 build" and the fork never
4087
+ the user forked "Example project 2.0 build" into "Example project 3.0 build" and the fork never
4054
4088
  appeared in the sessions list. It had not failed to index -- it was indexed AS ITS
4055
- PARENT. The list showed one `Markt POS 2.0 build` row whose modified time was the
4089
+ PARENT. The list showed one `Example project 2.0 build` row whose modified time was the
4056
4090
  fork's activity: two conversations, one identity, and the newer one invisible.
4057
4091
 
4058
4092
  Cloning copies the parent's `session_meta` record wholesale, so the new rollout carries
@@ -4078,7 +4112,7 @@ and a clone of a clone doubles again.
4078
4112
 
4079
4113
  ### The seeded query was being crowded out by the steps
4080
4114
 
4081
- Caught by probing the live stream after shipping 6.36.2, before Miles tested it: a
4115
+ Caught by probing the live stream after shipping 6.36.2, before the user tested it: a
4082
4116
  real session seeded 8 events -- 6 tool calls, one prose, one status -- and **no
4083
4117
  prompt**. The seed took "the last 7 events of any kind", and in a busy run the
4084
4118
  user's question is twenty or thirty steps back, so the activity you opened the page
@@ -4095,12 +4129,12 @@ scrolling window.
4095
4129
 
4096
4130
  ### The live view stops being a blank slate
4097
4131
 
4098
- Three changes, all from Miles watching a real session on hardware.
4132
+ Three changes, all from the user watching a real session on hardware.
4099
4133
 
4100
4134
  - **The user's query is now an event.** A `user` record used to be dropped whole,
4101
4135
  on the reasoning that "the prompt came from this device" -- true of a Continue
4102
4136
  turn and false of the case that matters most, a session running in a Mac window
4103
- where that record is the question Miles typed there and the glasses have never
4137
+ where that record is the question the user typed there and the glasses have never
4104
4138
  seen it. Dropping it is why the lens said WORKING and gave no clue what it was
4105
4139
  working ON. Tool results stay dropped; harness wrappers
4106
4140
  (`<system-reminder>`, `<cos-alarms>`, the memory and bulletin blocks) are
@@ -4129,7 +4163,7 @@ Needs COS Glasses 6.8.374 to render any of it.
4129
4163
 
4130
4164
  ### A reply keeps its line structure
4131
4165
 
4132
- Miles, from a G2 screenshot: a session reply arrived on the lens as one unbroken
4166
+ the user, from a G2 screenshot: a session reply arrived on the lens as one unbroken
4133
4167
  paragraph carrying three headings and six bullets, none of them visible.
4134
4168
 
4135
4169
  `proseBody` collapsed ALL whitespace, and both the one-line list gist and the
@@ -4197,7 +4231,7 @@ Needs COS Glasses 6.8.372 to be visible. An older app never calls the route.
4197
4231
 
4198
4232
  ### The newest assistant reply arrives whole instead of at 160 characters
4199
4233
 
4200
- - **The defect.** Miles opened a session on the glasses and the newest reply was cut
4234
+ - **The defect.** the user opened a session on the glasses and the newest reply was cut
4201
4235
  off mid-sentence. The session detail payload had NO full-text field: it carried
4202
4236
  `discussion_summary` (180 chars) and `discussion_digest` (2000), and the newest
4203
4237
  reply appeared only as the `Latest:` line inside the digest, produced by
@@ -4267,7 +4301,7 @@ Needs COS Glasses 6.8.372 to be visible. An older app never calls the route.
4267
4301
  ### A continued turn now runs with the session's own permissions
4268
4302
 
4269
4303
  - **This widens what Continue can do.** A prompt spoken into the glasses can run
4270
- tools on the Mac with nobody at the keyboard. Authorized explicitly by Miles
4304
+ tools on the Mac with nobody at the keyboard. Authorized explicitly by the user
4271
4305
  after his first real continued turn came back reporting that every tool was
4272
4306
  disabled, which is not the point of the feature.
4273
4307
  - 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)
@@ -0,0 +1,7 @@
1
+ {
2
+ "schema": 1,
3
+ "protocol": 1,
4
+ "version": "0.1.0",
5
+ "file": "cos-memory-runtime-0.1.0.tar.gz",
6
+ "sha256": "1f03e8d3bcc878709bae7580a8c685c1c5494839cebd7f2c79248dbc17e4d512"
7
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.44.15",
3
+ "version": "6.45.0",
4
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",
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 Miles
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. Miles, 2026-08-17: "if there's a session that's
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 Miles on 2026-08-18. Continue refused
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. Miles, 2026-08-12:
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 (Miles 2026-08-25). Absent key = on; only a
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'