@gotcos/glasses-server 6.12.6 → 6.12.7

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,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.12.7
4
+
5
+ Pairs the public server with COS Glasses build 227+ meeting follow-ups while
6
+ preserving every existing meeting response field and legacy client contract.
7
+
8
+ - **Meeting follow-ups receive canonical source context.** Authenticated
9
+ meeting-detail responses now add `sourceContent` and `sourceTruncated`, so
10
+ the glasses can attach the actual meeting record before a follow-up query.
11
+ - **Bounded and UTF-8 safe.** Source context is capped at 100 KB without
12
+ splitting a multibyte character. Existing summary and transcript fields are
13
+ unchanged.
14
+ - **Backward compatible.** Older clients ignore the additive fields; newer
15
+ clients no longer display the server-update warning on public installs.
16
+
3
17
  ## 6.12.6
4
18
 
5
19
  Pairs with COS Glasses build 222 to harden local-first meeting recovery and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.12.6",
3
+ "version": "6.12.7",
4
4
  "description": "COS Glasses — self-hosted AI heads-up-display server for Even G2 smart glasses, powered by your local Claude Code or Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,6 +28,7 @@ const SAFE_FILENAME_PATTERN = /^\d{4}-\d{2}-\d{2}_[A-Za-z0-9][A-Za-z0-9_-]{0,95}
28
28
  const DOMAIN_PATTERN = /^[a-z][a-z0-9_]{0,31}$/
29
29
  const MAX_MEETING_BYTES = 10 * 1024 * 1024
30
30
  const DETAIL_CHUNK_ESTIMATE_CHARS = 1_700
31
+ export const MEETING_SOURCE_MAX_BYTES = 100_000
31
32
 
32
33
  export class MeetingStoreError extends Error {
33
34
  constructor(
@@ -71,6 +72,10 @@ export interface MeetingDetail extends MeetingMeta {
71
72
  attendees: string[]
72
73
  /** Additive field for API consumers that want the exact canonical record. */
73
74
  transcript: string
75
+ /** Bounded canonical meeting markdown for model-grounded follow-up prompts. */
76
+ sourceContent: string
77
+ /** True when sourceContent is a UTF-8-safe prefix of a larger record. */
78
+ sourceTruncated: boolean
74
79
  }
75
80
 
76
81
  export interface SaveMeetingInput {
@@ -271,6 +276,7 @@ function parseMeeting(content: string, filename: string, month: string): Meeting
271
276
  const decisions = parseListSection(content, ['Decisions', 'Decisions Made'], 10)
272
277
  const actionItems = parseActions(content)
273
278
  const attendees = parseAttendees(content)
279
+ const source = boundedMeetingSource(content)
274
280
 
275
281
  return {
276
282
  filename,
@@ -288,9 +294,22 @@ function parseMeeting(content: string, filename: string, month: string): Meeting
288
294
  actionItems,
289
295
  attendees,
290
296
  transcript,
297
+ ...source,
291
298
  }
292
299
  }
293
300
 
301
+ /** Return enough canonical source for grounded meeting follow-ups without
302
+ * allowing an unexpectedly large archive record to inflate every response.
303
+ * The boundary backs up over UTF-8 continuation bytes so the prefix never
304
+ * ends with a replacement character. */
305
+ export function boundedMeetingSource(content: string): { sourceContent: string; sourceTruncated: boolean } {
306
+ const bytes = Buffer.from(content, 'utf8')
307
+ if (bytes.length <= MEETING_SOURCE_MAX_BYTES) return { sourceContent: content, sourceTruncated: false }
308
+ let end = MEETING_SOURCE_MAX_BYTES
309
+ while (end > 0 && (bytes[end] & 0xc0) === 0x80) end -= 1
310
+ return { sourceContent: bytes.subarray(0, end).toString('utf8'), sourceTruncated: true }
311
+ }
312
+
294
313
  function toMeta(detail: MeetingDetail): MeetingMeta {
295
314
  const detailCharEstimate = [
296
315
  detail.title,