@gotcos/glasses-server 6.36.2 → 6.36.4

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
@@ -2219,6 +2219,51 @@ unsaved capture, and makes batch status stop lying about finished work.
2219
2219
 
2220
2220
  # Changelog
2221
2221
 
2222
+ ## [6.36.4] - 2026-08-17
2223
+
2224
+ ### A cloned Codex conversation gets its own identity
2225
+
2226
+ Miles forked "Markt POS 2.0 build" into "POS Nation 3.0 build" and the fork never
2227
+ appeared in the sessions list. It had not failed to index -- it was indexed AS ITS
2228
+ PARENT. The list showed one `Markt POS 2.0 build` row whose modified time was the
2229
+ fork's activity: two conversations, one identity, and the newer one invisible.
2230
+
2231
+ Cloning copies the parent's `session_meta` record wholesale, so the new rollout carries
2232
+ the PARENT's id under its own filename. `listCodexSessions` keyed on `meta.id` and
2233
+ folded them together.
2234
+
2235
+ The filename is unique per rollout by construction; the meta record is content and can
2236
+ be a copy. **When they disagree, the filename now wins.** An ordinary session, where
2237
+ they agree, is untouched.
2238
+
2239
+ Verified against the real machine before and after: the fork's `session_meta.id` reads
2240
+ `019e0943…` (the parent) while its filename reads `01a0119c…`.
2241
+
2242
+ ### Also worth recording, not fixed here
2243
+
2244
+ That fork's rollout is **771,177,205 bytes** -- 735 MB, against other rollouts small
2245
+ enough not to register. The clone duplicated the entire parent transcript and Codex
2246
+ rewrites the whole file, which is past Node's 512 MB string limit; COS survives only
2247
+ because it reads bounded windows. Giving the fork its own identity does not shrink it,
2248
+ and a clone of a clone doubles again.
2249
+
2250
+ ## [6.36.3] - 2026-08-17
2251
+
2252
+ ### The seeded query was being crowded out by the steps
2253
+
2254
+ Caught by probing the live stream after shipping 6.36.2, before Miles tested it: a
2255
+ real session seeded 8 events -- 6 tool calls, one prose, one status -- and **no
2256
+ prompt**. The seed took "the last 7 events of any kind", and in a busy run the
2257
+ user's question is twenty or thirty steps back, so the activity you opened the page
2258
+ to watch is exactly what pushed the query off it. The one case the feature exists
2259
+ for was the one case it failed.
2260
+
2261
+ The newest prompt in the read window is now emitted FIRST and unconditionally,
2262
+ outside the step budget. Measured on this Mac's largest transcript (87.2 MB): the
2263
+ last 256 KiB holds 133 records including 3 user turns, so the window reaches a query
2264
+ comfortably. The client pins rather than lists it, so it costs nothing in the
2265
+ scrolling window.
2266
+
2222
2267
  ## [6.36.2] - 2026-08-17
2223
2268
 
2224
2269
  ### The live view stops being a blank slate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.36.2",
3
+ "version": "6.36.4",
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": {
@@ -899,7 +899,26 @@ export async function listCodexSessions(
899
899
  if (rows.length >= Math.max(0, limit)) break
900
900
  const meta = await peekCodexMeta(candidate.file)
901
901
  if (!meta || meta.subagent) continue
902
- const native = meta.id || candidate.name.slice(0, -6)
902
+ // THE FILENAME WINS WHEN THEY DISAGREE.
903
+ //
904
+ // A rollout's `session_meta.id` is normally its own id, and normally the filename
905
+ // agrees. A CLONED conversation breaks that: the clone copies the parent's
906
+ // `session_meta` record wholesale, so the new rollout carries the PARENT's id
907
+ // while its filename carries its own.
908
+ //
909
+ // Miles forked "Markt POS 2.0 build" into "POS Nation 3.0 build" and the fork
910
+ // never appeared in the session list. It had not failed to index -- it was indexed
911
+ // AS its parent, so the list showed one `Markt POS 2.0 build` row whose modified
912
+ // time was the fork's activity. Two conversations, one identity, and the newer one
913
+ // invisible.
914
+ //
915
+ // The filename is unique per rollout by construction; the meta record is content
916
+ // and can be a copy. So when they disagree, trust the filename.
917
+ const fileId = idFromCodexFilename(candidate.name)
918
+ const metaId = (meta.id || '').trim().toLowerCase()
919
+ const native = fileId && metaId && fileId !== metaId
920
+ ? fileId
921
+ : (meta.id || candidate.name.slice(0, -6))
903
922
  const title = names.get(native) || meta.title || 'Codex session'
904
923
  if (isKeepWarmSessionTitle(title)) continue
905
924
  const created = meta.created || createdFromCodexFilename(candidate.name) || isoFromMtime(candidate.birthtimeMs)
@@ -264,7 +264,21 @@ agentSessionStreamRouter.get('/agent-sessions/:provider/:sessionId/stream', asyn
264
264
  // Status drafts are dropped from the seed: they describe the state at some past
265
265
  // moment and the opening status above is the CURRENT one. Replaying an old
266
266
  // `done` after it would tell the client the live session had finished.
267
- const steps = drafts.filter(d => d.kind === 'tool' || d.kind === 'prose' || d.kind === 'prompt')
267
+ // THE QUERY IS SEEDED SEPARATELY, AND ALWAYS.
268
+ //
269
+ // Taking "the last 7 events" and hoping the prompt is among them does not work,
270
+ // and a live probe against a real session proved it: 8 seeded events, 6 tools and
271
+ // one prose, and NO prompt -- because in a busy run the user's question is twenty
272
+ // or thirty steps back and gets pushed out of the window by the very activity you
273
+ // opened the page to watch. That is the exact case the query exists for.
274
+ //
275
+ // So the newest prompt in the whole read window is emitted FIRST, unconditionally,
276
+ // and the step budget is spent entirely on steps. The client pins it rather than
277
+ // listing it, so it costs nothing in the scrolling window.
278
+ const lastPrompt = [...drafts].reverse().find(d => d.kind === 'prompt')
279
+ if (lastPrompt) write({ ...lastPrompt, at: Date.now() })
280
+
281
+ const steps = drafts.filter(d => d.kind === 'tool' || d.kind === 'prose')
268
282
  for (const draft of steps.slice(-SEED_EVENTS)) {
269
283
  // NOT tagged as seeded. A replayed step is a step that really happened, and a
270
284
  // second rendering style for it would be a distinction without a use. The one