@gotcos/glasses-server 6.24.2 → 6.24.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 +58 -0
- package/README.md +10 -3
- package/package.json +1 -1
- package/server/index.ts +2 -1
- package/server/lib/claude-bridge.ts +19 -5
- package/server/lib/codex-bridge.ts +14 -5
- package/server/lib/context-builder.ts +1 -0
- package/server/lib/cursor-bridge.ts +9 -4
- package/server/lib/media-store.ts +139 -4
- package/server/lib/prompt-reference-boundary.ts +24 -0
- package/server/lib/quarantine-auto-recover.ts +30 -4
- package/server/lib/query-attachments.ts +62 -4
- package/server/lib/query-job-runtime.ts +8 -3
- package/server/lib/rich-media-safety.ts +324 -0
- package/server/routes/health.ts +15 -1
- package/server/routes/media.ts +46 -1
- package/server/routes/meeting.ts +2 -1
- package/server/routes/query.ts +5 -3
- package/server/routes/transcribe-stream.ts +15 -1
- package/shared/media-attachment.ts +84 -4
|
@@ -5,9 +5,20 @@
|
|
|
5
5
|
// server media index. Anything that persists or transmits an attachment
|
|
6
6
|
// persists THIS shape (or just the id) and nothing else.
|
|
7
7
|
|
|
8
|
-
export type MediaKind = 'user_photo' | 'traffic_frame' | 'generated_visual'
|
|
8
|
+
export type MediaKind = 'user_photo' | 'user_document' | 'user_video' | 'traffic_frame' | 'generated_visual'
|
|
9
9
|
|
|
10
|
-
export type
|
|
10
|
+
export type MediaCategory = 'image' | 'document' | 'video'
|
|
11
|
+
|
|
12
|
+
export type MediaMime =
|
|
13
|
+
| 'image/jpeg'
|
|
14
|
+
| 'image/png'
|
|
15
|
+
| 'text/plain'
|
|
16
|
+
| 'text/markdown'
|
|
17
|
+
| 'text/csv'
|
|
18
|
+
| 'application/json'
|
|
19
|
+
| 'application/pdf'
|
|
20
|
+
| 'video/mp4'
|
|
21
|
+
| 'video/quicktime'
|
|
11
22
|
|
|
12
23
|
export interface MediaAttachmentRef {
|
|
13
24
|
id: string
|
|
@@ -16,6 +27,14 @@ export interface MediaAttachmentRef {
|
|
|
16
27
|
width: number
|
|
17
28
|
height: number
|
|
18
29
|
createdAt: string
|
|
30
|
+
/** Additive discriminator. Legacy image refs deliberately omit it so
|
|
31
|
+
* durable request fingerprints remain byte-compatible. */
|
|
32
|
+
category?: MediaCategory
|
|
33
|
+
bytes?: number
|
|
34
|
+
durationMs?: number
|
|
35
|
+
frameCount?: number
|
|
36
|
+
textChars?: number
|
|
37
|
+
truncated?: boolean
|
|
19
38
|
label?: string
|
|
20
39
|
capturedAt?: string
|
|
21
40
|
expiresAt?: string
|
|
@@ -36,8 +55,15 @@ export function isValidMediaId(id: unknown): id is string {
|
|
|
36
55
|
return typeof id === 'string' && MEDIA_ID_RE.test(id)
|
|
37
56
|
}
|
|
38
57
|
|
|
39
|
-
const VALID_KINDS: ReadonlySet<string> = new Set([
|
|
40
|
-
|
|
58
|
+
const VALID_KINDS: ReadonlySet<string> = new Set([
|
|
59
|
+
'user_photo', 'user_document', 'user_video', 'traffic_frame', 'generated_visual',
|
|
60
|
+
])
|
|
61
|
+
const IMAGE_MIMES: ReadonlySet<string> = new Set(['image/jpeg', 'image/png'])
|
|
62
|
+
const DOCUMENT_MIMES: ReadonlySet<string> = new Set([
|
|
63
|
+
'text/plain', 'text/markdown', 'text/csv', 'application/json', 'application/pdf',
|
|
64
|
+
])
|
|
65
|
+
const VIDEO_MIMES: ReadonlySet<string> = new Set(['video/mp4', 'video/quicktime'])
|
|
66
|
+
const VALID_MIMES: ReadonlySet<string> = new Set([...IMAGE_MIMES, ...DOCUMENT_MIMES, ...VIDEO_MIMES])
|
|
41
67
|
const MAX_LABEL_LEN = 120
|
|
42
68
|
// ISO-8601 subset — what `new Date().toISOString()` emits.
|
|
43
69
|
const ISO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/
|
|
@@ -62,6 +88,15 @@ export function parseMediaAttachmentRef(raw: unknown): MediaAttachmentRef | null
|
|
|
62
88
|
if (typeof r.mime !== 'string' || !VALID_MIMES.has(r.mime)) return null
|
|
63
89
|
if (!isDimension(r.width) || !isDimension(r.height)) return null
|
|
64
90
|
if (!isIsoTimestamp(r.createdAt)) return null
|
|
91
|
+
const inferredCategory = r.kind === 'user_document'
|
|
92
|
+
? 'document'
|
|
93
|
+
: r.kind === 'user_video'
|
|
94
|
+
? 'video'
|
|
95
|
+
: 'image'
|
|
96
|
+
if ((inferredCategory === 'image' && !IMAGE_MIMES.has(r.mime))
|
|
97
|
+
|| (inferredCategory === 'document' && !DOCUMENT_MIMES.has(r.mime))
|
|
98
|
+
|| (inferredCategory === 'video' && !VIDEO_MIMES.has(r.mime))) return null
|
|
99
|
+
if (r.category !== undefined && r.category !== inferredCategory) return null
|
|
65
100
|
const ref: MediaAttachmentRef = {
|
|
66
101
|
id: r.id,
|
|
67
102
|
kind: r.kind as MediaKind,
|
|
@@ -70,6 +105,22 @@ export function parseMediaAttachmentRef(raw: unknown): MediaAttachmentRef | null
|
|
|
70
105
|
height: r.height,
|
|
71
106
|
createdAt: r.createdAt,
|
|
72
107
|
}
|
|
108
|
+
// Do not add category to legacy image refs that never carried it. Their
|
|
109
|
+
// canonical JSON is part of persisted durable-query fingerprints.
|
|
110
|
+
if (r.category === inferredCategory) ref.category = inferredCategory
|
|
111
|
+
if (typeof r.bytes === 'number' && Number.isSafeInteger(r.bytes) && r.bytes >= 0 && r.bytes <= 64 * 1024 * 1024) {
|
|
112
|
+
ref.bytes = r.bytes
|
|
113
|
+
}
|
|
114
|
+
if (typeof r.durationMs === 'number' && Number.isSafeInteger(r.durationMs) && r.durationMs >= 0 && r.durationMs <= 60 * 60_000) {
|
|
115
|
+
ref.durationMs = r.durationMs
|
|
116
|
+
}
|
|
117
|
+
if (typeof r.frameCount === 'number' && Number.isSafeInteger(r.frameCount) && r.frameCount >= 0 && r.frameCount <= 8) {
|
|
118
|
+
ref.frameCount = r.frameCount
|
|
119
|
+
}
|
|
120
|
+
if (typeof r.textChars === 'number' && Number.isSafeInteger(r.textChars) && r.textChars >= 0 && r.textChars <= 100_000) {
|
|
121
|
+
ref.textChars = r.textChars
|
|
122
|
+
}
|
|
123
|
+
if (r.truncated === true) ref.truncated = true
|
|
73
124
|
if (typeof r.label === 'string' && r.label.length > 0) {
|
|
74
125
|
ref.label = r.label.slice(0, MAX_LABEL_LEN)
|
|
75
126
|
}
|
|
@@ -78,6 +129,35 @@ export function parseMediaAttachmentRef(raw: unknown): MediaAttachmentRef | null
|
|
|
78
129
|
return ref
|
|
79
130
|
}
|
|
80
131
|
|
|
132
|
+
export function mediaCategoryOf(ref: Pick<MediaAttachmentRef, 'kind' | 'category'>): MediaCategory {
|
|
133
|
+
if (ref.category) return ref.category
|
|
134
|
+
if (ref.kind === 'user_document') return 'document'
|
|
135
|
+
if (ref.kind === 'user_video') return 'video'
|
|
136
|
+
return 'image'
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function isImageAttachmentRef(ref: MediaAttachmentRef): boolean {
|
|
140
|
+
return mediaCategoryOf(ref) === 'image'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function attachmentHistoryPrefix(refs: readonly MediaAttachmentRef[]): string {
|
|
144
|
+
if (refs.length === 0) return ''
|
|
145
|
+
if (refs.length > 1) return `[${refs.length} Attachments]`
|
|
146
|
+
const category = mediaCategoryOf(refs[0])
|
|
147
|
+
return category === 'image' ? '[Photo]' : category === 'video' ? '[Video]' : '[File]'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function defaultAttachmentRequest(refs: readonly MediaAttachmentRef[]): string {
|
|
151
|
+
if (refs.length === 0) return ''
|
|
152
|
+
const categories = new Set(refs.map(mediaCategoryOf))
|
|
153
|
+
if (categories.size > 1 || refs.length > 1) return 'Review these attachments.'
|
|
154
|
+
return categories.has('document')
|
|
155
|
+
? 'Summarize this file.'
|
|
156
|
+
: categories.has('video')
|
|
157
|
+
? 'Review this video.'
|
|
158
|
+
: 'What do you see?'
|
|
159
|
+
}
|
|
160
|
+
|
|
81
161
|
/** Validate an untrusted array of refs, dropping only the invalid entries
|
|
82
162
|
* (a bad ref must never take the whole conversation record with it). */
|
|
83
163
|
export function parseMediaAttachmentRefs(raw: unknown): MediaAttachmentRef[] {
|