@gotcos/glasses-server 6.94.4 → 6.98.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.
- package/CHANGELOG.md +147 -0
- package/README.md +52 -0
- package/managed-runtime-contract.json +13 -0
- package/package.json +3 -1
- package/server/index.ts +19 -1
- package/server/lib/attached-provider-adapter.ts +35 -1
- package/server/lib/claude-bridge.ts +47 -0
- package/server/lib/codex-bridge.ts +11 -1
- package/server/lib/codex-rollout-watermark.ts +42 -0
- package/server/lib/cursor-bridge.ts +2 -1
- package/server/lib/dictation-clean-models.ts +2 -0
- package/server/lib/dictation-clean.ts +2 -2
- package/server/lib/dictation-polish-pipeline.ts +22 -2
- package/server/lib/dictation-prewarm.ts +16 -0
- package/server/lib/direct-bridge.ts +14 -2
- package/server/lib/direct-sse.ts +6 -1
- package/server/lib/display-bus.ts +1 -1
- package/server/lib/fork-thread.ts +32 -1
- package/server/lib/held-naming-batches.ts +36 -0
- package/server/lib/job-audit-caller.ts +20 -0
- package/server/lib/lens-gist-engines.ts +17 -5
- package/server/lib/live-cos-ask.ts +29 -6
- package/server/lib/live-cue-verdicts.ts +43 -8
- package/server/lib/live-cues-engine.ts +4 -1
- package/server/lib/live-cues-filter.ts +50 -0
- package/server/lib/live-cues-general-prompts.ts +137 -0
- package/server/lib/live-cues-general-topic.ts +245 -0
- package/server/lib/live-cues-general.ts +715 -0
- package/server/lib/live-cues-v2-data.ts +453 -0
- package/server/lib/live-cues-v2-grounded.ts +262 -0
- package/server/lib/live-cues-v2-judge-b.ts +59 -0
- package/server/lib/live-cues-v2-names.ts +49 -0
- package/server/lib/live-cues-v2-notes.ts +8 -4
- package/server/lib/live-cues-v2-open-items.ts +132 -0
- package/server/lib/live-cues-v2-pacing.ts +45 -0
- package/server/lib/live-cues-v2-prompts-kinds.ts +9 -4
- package/server/lib/live-cues-v2-settings.ts +89 -10
- package/server/lib/live-cues-v2.ts +570 -47
- package/server/lib/mac-recording-asr.ts +6 -3
- package/server/lib/mac-speaker-call-human.ts +371 -0
- package/server/lib/mac-speaker-call-interval-human.ts +499 -0
- package/server/lib/mac-speaker-call-interval-projection.ts +233 -0
- package/server/lib/mac-speaker-call-original-view.ts +328 -0
- package/server/lib/mac-speaker-call-producer-worker.ts +615 -0
- package/server/lib/mac-speaker-call-producer.ts +539 -0
- package/server/lib/mac-speaker-call-store.ts +264 -0
- package/server/lib/mac-speaker-call-view.ts +373 -0
- package/server/lib/mac-speaker-live-compute.ts +72 -0
- package/server/lib/mac-speaker-live-memory.ts +414 -0
- package/server/lib/mac-speaker-live-projection.ts +126 -0
- package/server/lib/mac-speaker-live-save.ts +90 -0
- package/server/lib/mac-speaker-live-store.ts +71 -0
- package/server/lib/mac-speaker-live-worker.ts +58 -0
- package/server/lib/mac-speaker-live.ts +175 -0
- package/server/lib/mac-speaker-profile-extractor.ts +15 -10
- package/server/lib/mac-speaker-shadow-repair.ts +307 -0
- package/server/lib/mac-voice-assignment.ts +44 -26
- package/server/lib/mac-voice-display.ts +195 -0
- package/server/lib/mac-voice-naming-batch.ts +1067 -0
- package/server/lib/mac-voice-suggestions.ts +548 -0
- package/server/lib/maintenance-lifecycle.ts +2 -0
- package/server/lib/media-store.ts +2 -1
- package/server/lib/meeting-ask-pasted.ts +135 -0
- package/server/lib/meeting-ask-review.ts +8 -1
- package/server/lib/meeting-ask-runner.ts +4 -1
- package/server/lib/meeting-ask-runtime.ts +189 -17
- package/server/lib/meeting-ask-store.ts +2 -0
- package/server/lib/meeting-ask-web.ts +284 -13
- package/server/lib/meeting-batch-progress.ts +51 -5
- package/server/lib/meeting-batch-transcribe.ts +35 -2
- package/server/lib/meeting-fact-check.ts +3 -0
- package/server/lib/meeting-finalization-jobs.ts +69 -2
- package/server/lib/meeting-public-topic.ts +6 -1
- package/server/lib/meeting-speaker-review.ts +182 -17
- package/server/lib/meeting-store.ts +6 -1
- package/server/lib/parakeet.ts +51 -5
- package/server/lib/pending-batch-retention.ts +58 -0
- package/server/lib/private-reference.ts +38 -0
- package/server/lib/provider-usage.ts +561 -0
- package/server/lib/public-https-url.ts +32 -0
- package/server/lib/query-job-runtime.ts +3 -0
- package/server/lib/rich-media-safety.ts +23 -2
- package/server/lib/speaker-shadow-decide.ts +503 -0
- package/server/lib/speaker-shadow-models.ts +150 -0
- package/server/lib/speaker-shadow-numeric.ts +143 -0
- package/server/lib/speaker-shadow-pair-source.ts +91 -0
- package/server/lib/speaker-shadow-paired.ts +268 -0
- package/server/lib/speaker-shadow-report.ts +113 -0
- package/server/lib/speaker-shadow-timeline.ts +210 -0
- package/server/lib/speaker-shadow-worker.ts +312 -0
- package/server/lib/speaker-shadow.ts +788 -0
- package/server/lib/token-audit.ts +36 -6
- package/server/lib/transcript-cleanup.ts +3 -2
- package/server/lib/voice-directory.ts +8 -4
- package/server/lib/web-lookup-ledger.ts +151 -0
- package/server/lib/work-review-runtime.ts +2 -0
- package/server/lib/work-search.ts +3 -1
- package/server/lib/xlsx-text.ts +277 -0
- package/server/routes/health.ts +5 -0
- package/server/routes/live-cues.ts +36 -14
- package/server/routes/meeting.ts +506 -49
- package/server/routes/speaker-shadow.ts +79 -0
- package/server/routes/transcribe-stream.ts +114 -33
- package/server/routes/voice.ts +3 -0
- package/server/scripts/live-cues-v3-mutation-gate.mjs +311 -0
- package/server/scripts/mac-voice-assignment-6930-mutation-gate.mjs +5 -5
- package/shared/live-cues-v2.ts +38 -5
- package/shared/media-attachment.ts +2 -0
- package/shared/meeting-ask.ts +11 -0
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
// var for a limit wins over the saved value and locks it. A file holding only `enabled` is still written as schema 1, so
|
|
12
12
|
// a 6.79.1 server reads it exactly as before; any limit makes it schema 2, which 6.79.1 also reads (it looks only at
|
|
13
13
|
// `enabled`).
|
|
14
|
+
//
|
|
15
|
+
// Live cues v3: the same file also holds the cue mode (focused | general | all), grounded cues (classic | grounded) and the
|
|
16
|
+
// six general-cue limits. A file holding any of them is schema 3; an older server that rewrites the file drops them, which
|
|
17
|
+
// reads as focused and classic, the safe values. A saved value that is malformed or outside its enum or bounds reads as
|
|
18
|
+
// unset. Env can only force the SAFE value (COS_LIVE_CUES_MODE=focused, COS_LIVE_CUES_GROUNDING=classic); any other value
|
|
19
|
+
// defers to the setting, so a managed env can never switch general or grounded cues on behind COS Control's back.
|
|
14
20
|
|
|
15
21
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs'
|
|
16
22
|
import { dirname } from 'node:path'
|
|
@@ -30,25 +36,63 @@ export type LiveCuesV2LimitName = keyof typeof LIVE_CUES_V2_LIMIT_BOUNDS
|
|
|
30
36
|
export const LIVE_CUES_V2_LIMIT_NAMES = Object.keys(LIVE_CUES_V2_LIMIT_BOUNDS) as LiveCuesV2LimitName[]
|
|
31
37
|
export type LiveCuesV2StoredLimits = Partial<Record<LiveCuesV2LimitName, number>>
|
|
32
38
|
|
|
39
|
+
/** Live cues v3 (cue mode). `focused` is today's cues; `general` adds nothing to focused cues but runs only insight cues; `all` runs both. */
|
|
40
|
+
export const LIVE_CUES_CUE_MODES = ['focused', 'general', 'all'] as const
|
|
41
|
+
export type LiveCuesCueMode = typeof LIVE_CUES_CUE_MODES[number]
|
|
42
|
+
export const LIVE_CUES_GROUNDINGS = ['classic', 'grounded'] as const
|
|
43
|
+
export type LiveCuesGrounding = typeof LIVE_CUES_GROUNDINGS[number]
|
|
44
|
+
export const isCueMode = (v: unknown): v is LiveCuesCueMode => typeof v === 'string' && (LIVE_CUES_CUE_MODES as readonly string[]).includes(v)
|
|
45
|
+
export const isGrounding = (v: unknown): v is LiveCuesGrounding => typeof v === 'string' && (LIVE_CUES_GROUNDINGS as readonly string[]).includes(v)
|
|
46
|
+
|
|
47
|
+
/** The six general-cue limits. `field` is the name on the wire (POST body and settings file); the key is the name in the
|
|
48
|
+
* `general.limits` view. Bounds are inclusive; integer fields take whole numbers only. */
|
|
49
|
+
export const LIVE_CUES_GENERAL_LIMIT_BOUNDS = {
|
|
50
|
+
intervalSeconds: { field: 'generalIntervalSeconds', min: 120, max: 1800, default: 300, integer: true, env: 'COS_LIVE_CUES_GENERAL_INTERVAL_S' },
|
|
51
|
+
trigger: { field: 'generalTrigger', min: 0.4, max: 0.9, default: 0.6, integer: false, env: 'COS_LIVE_CUES_GENERAL_TRIGGER' },
|
|
52
|
+
attemptsPerMeeting: { field: 'generalAttemptsPerMeeting', min: 1, max: 30, default: 8, integer: true, env: 'COS_LIVE_CUES_GENERAL_ATTEMPTS' },
|
|
53
|
+
maxCuesPerMeeting: { field: 'generalMaxCuesPerMeeting', min: 1, max: 12, default: 4, integer: true, env: 'COS_LIVE_CUES_GENERAL_MAX_PER_MEETING' },
|
|
54
|
+
dailyTopicCalls: { field: 'generalDailyTopicCalls', min: 1, max: 200, default: 60, integer: true, env: 'COS_LIVE_CUES_GENERAL_DAILY_TOPIC_CALLS' },
|
|
55
|
+
dailyWebSearches: { field: 'generalDailyWebSearches', min: 1, max: 100, default: 30, integer: true, env: 'COS_LIVE_CUES_GENERAL_DAILY_WEB_SEARCHES' },
|
|
56
|
+
} as const
|
|
57
|
+
export type LiveCuesGeneralLimitName = keyof typeof LIVE_CUES_GENERAL_LIMIT_BOUNDS
|
|
58
|
+
export const LIVE_CUES_GENERAL_LIMIT_NAMES = Object.keys(LIVE_CUES_GENERAL_LIMIT_BOUNDS) as LiveCuesGeneralLimitName[]
|
|
59
|
+
export type LiveCuesGeneralStoredLimits = Partial<Record<LiveCuesGeneralLimitName, number>>
|
|
60
|
+
export function generalLimitInBounds(name: LiveCuesGeneralLimitName, value: unknown): value is number {
|
|
61
|
+
return withinBounds(LIVE_CUES_GENERAL_LIMIT_BOUNDS[name], value)
|
|
62
|
+
}
|
|
63
|
+
|
|
33
64
|
/** Inside the bounds, and whole where the field is whole. */
|
|
34
|
-
|
|
35
|
-
const b = LIVE_CUES_V2_LIMIT_BOUNDS[name]
|
|
65
|
+
function withinBounds(b: { min: number; max: number; integer: boolean }, value: unknown): value is number {
|
|
36
66
|
return typeof value === 'number' && Number.isFinite(value) && value >= b.min && value <= b.max && (!b.integer || Number.isInteger(value))
|
|
37
67
|
}
|
|
68
|
+
export function limitInBounds(name: LiveCuesV2LimitName, value: unknown): value is number {
|
|
69
|
+
return withinBounds(LIVE_CUES_V2_LIMIT_BOUNDS[name], value)
|
|
70
|
+
}
|
|
38
71
|
|
|
39
72
|
/** What the engine and the routes need from the store: the saved choice, or null when there is none, and (6.79.2) the
|
|
40
73
|
* saved limits. `limits` is optional so a reader without it means "no saved limits". */
|
|
41
|
-
export interface LiveCuesV2SettingReader {
|
|
74
|
+
export interface LiveCuesV2SettingReader {
|
|
75
|
+
stored(): boolean | null
|
|
76
|
+
limits?(): LiveCuesV2StoredLimits
|
|
77
|
+
/** Live cues v3, all optional: a reader without them means focused, classic and the default general limits. */
|
|
78
|
+
cueMode?(): LiveCuesCueMode | null
|
|
79
|
+
grounding?(): LiveCuesGrounding | null
|
|
80
|
+
generalLimits?(): LiveCuesGeneralStoredLimits
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface Loaded { value: boolean | null; limits: LiveCuesV2StoredLimits; cueMode: LiveCuesCueMode | null; grounding: LiveCuesGrounding | null; general: LiveCuesGeneralStoredLimits }
|
|
42
84
|
|
|
43
85
|
export class LiveCuesV2Settings implements LiveCuesV2SettingReader {
|
|
44
86
|
/** Loaded once; only this server writes the file, and every write goes through update(). */
|
|
45
|
-
private cache:
|
|
87
|
+
private cache: Loaded | null = null
|
|
46
88
|
constructor(readonly file: string) {}
|
|
47
89
|
|
|
48
|
-
private load():
|
|
90
|
+
private load(): Loaded {
|
|
49
91
|
if (this.cache) return this.cache
|
|
50
92
|
let value: boolean | null = null
|
|
51
93
|
const limits: LiveCuesV2StoredLimits = {}
|
|
94
|
+
let cueMode: LiveCuesCueMode | null = null, grounding: LiveCuesGrounding | null = null
|
|
95
|
+
const general: LiveCuesGeneralStoredLimits = {}
|
|
52
96
|
try {
|
|
53
97
|
if (existsSync(this.file)) {
|
|
54
98
|
const raw = JSON.parse(readFileSync(this.file, 'utf8')) as unknown
|
|
@@ -56,29 +100,46 @@ export class LiveCuesV2Settings implements LiveCuesV2SettingReader {
|
|
|
56
100
|
const r = raw as Record<string, unknown>
|
|
57
101
|
if (typeof r.enabled === 'boolean') value = r.enabled
|
|
58
102
|
for (const name of LIVE_CUES_V2_LIMIT_NAMES) if (limitInBounds(name, r[name])) limits[name] = r[name] as number
|
|
103
|
+
if (isCueMode(r.cueMode)) cueMode = r.cueMode
|
|
104
|
+
if (isGrounding(r.grounding)) grounding = r.grounding
|
|
105
|
+
for (const name of LIVE_CUES_GENERAL_LIMIT_NAMES) {
|
|
106
|
+
const field = LIVE_CUES_GENERAL_LIMIT_BOUNDS[name].field
|
|
107
|
+
if (generalLimitInBounds(name, r[field])) general[name] = r[field] as number
|
|
108
|
+
}
|
|
59
109
|
}
|
|
60
110
|
}
|
|
61
111
|
} catch { value = null }
|
|
62
|
-
this.cache = { value, limits }
|
|
112
|
+
this.cache = { value, limits, cueMode, grounding, general }
|
|
63
113
|
return this.cache
|
|
64
114
|
}
|
|
65
115
|
|
|
66
116
|
stored(): boolean | null { return this.load().value }
|
|
67
117
|
limits(): LiveCuesV2StoredLimits { return { ...this.load().limits } }
|
|
118
|
+
cueMode(): LiveCuesCueMode | null { return this.load().cueMode }
|
|
119
|
+
grounding(): LiveCuesGrounding | null { return this.load().grounding }
|
|
120
|
+
generalLimits(): LiveCuesGeneralStoredLimits { return { ...this.load().general } }
|
|
68
121
|
|
|
69
122
|
/** Atomic and durable. The in-memory value changes only after the file is written, so a failed write changes nothing. */
|
|
70
123
|
save(enabled: boolean, now: Date = new Date()): void { this.update({ enabled }, now) }
|
|
71
124
|
|
|
72
|
-
/** One write for any mix of the switch and the
|
|
73
|
-
|
|
125
|
+
/** One write for any mix of the switch, the limits and (v3) the cue mode, grounding and general limits; unnamed fields
|
|
126
|
+
* keep their saved values, the v3 ones included. */
|
|
127
|
+
update(patch: { enabled?: boolean; limits?: LiveCuesV2StoredLimits; cueMode?: LiveCuesCueMode; grounding?: LiveCuesGrounding; general?: LiveCuesGeneralStoredLimits },
|
|
128
|
+
now: Date = new Date()): void {
|
|
74
129
|
const current = this.load()
|
|
75
130
|
const value = patch.enabled ?? current.value
|
|
76
131
|
const limits = { ...current.limits, ...patch.limits }
|
|
132
|
+
const cueMode = patch.cueMode ?? current.cueMode
|
|
133
|
+
const grounding = patch.grounding ?? current.grounding
|
|
134
|
+
const general = { ...current.general, ...patch.general }
|
|
77
135
|
const hasLimits = Object.keys(limits).length > 0
|
|
78
|
-
const
|
|
136
|
+
const hasV3 = cueMode !== null || grounding !== null || Object.keys(general).length > 0
|
|
137
|
+
const generalFields = Object.fromEntries(LIVE_CUES_GENERAL_LIMIT_NAMES.filter(n => general[n] !== undefined).map(n => [LIVE_CUES_GENERAL_LIMIT_BOUNDS[n].field, general[n]]))
|
|
138
|
+
const body = { schema: hasV3 ? 3 : hasLimits ? 2 : 1, ...(value === null ? {} : { enabled: value }), ...limits,
|
|
139
|
+
...(cueMode === null ? {} : { cueMode }), ...(grounding === null ? {} : { grounding }), ...generalFields, updatedAt: now.toISOString() }
|
|
79
140
|
mkdirSync(dirname(this.file), { recursive: true, mode: 0o700 })
|
|
80
141
|
durableAtomicWriteFileSync(this.file, JSON.stringify(body) + '\n', { mode: 0o600 })
|
|
81
|
-
this.cache = { value, limits }
|
|
142
|
+
this.cache = { value, limits, cueMode, grounding, general }
|
|
82
143
|
}
|
|
83
144
|
}
|
|
84
145
|
|
|
@@ -94,6 +155,24 @@ export function resolveLiveCuesV2(env: NodeJS.ProcessEnv, settings: LiveCuesV2Se
|
|
|
94
155
|
return { enabled: false, source: 'default' }
|
|
95
156
|
}
|
|
96
157
|
|
|
158
|
+
/** Where a v3 mode came from. `env` only ever means the safe value was forced, so it is locked. */
|
|
159
|
+
export interface LiveCuesV2Mode<T extends string> { value: T; source: LiveCuesV2SettingSource; locked: boolean }
|
|
160
|
+
/** COS_LIVE_CUES_MODE=focused forces focused; any other env value, a missing or malformed saved value, or a reader that
|
|
161
|
+
* throws, reads through to the saved mode, then to focused. */
|
|
162
|
+
export function resolveLiveCuesCueMode(env: NodeJS.ProcessEnv, settings: LiveCuesV2SettingReader | null): LiveCuesV2Mode<LiveCuesCueMode> {
|
|
163
|
+
if (env.COS_LIVE_CUES_MODE === 'focused') return { value: 'focused', source: 'env', locked: true }
|
|
164
|
+
let stored: LiveCuesCueMode | null = null
|
|
165
|
+
try { const v = settings?.cueMode?.(); stored = isCueMode(v) ? v : null } catch { stored = null }
|
|
166
|
+
return stored !== null ? { value: stored, source: 'setting', locked: false } : { value: 'focused', source: 'default', locked: false }
|
|
167
|
+
}
|
|
168
|
+
/** COS_LIVE_CUES_GROUNDING=classic forces classic, the same way. */
|
|
169
|
+
export function resolveLiveCuesGrounding(env: NodeJS.ProcessEnv, settings: LiveCuesV2SettingReader | null): LiveCuesV2Mode<LiveCuesGrounding> {
|
|
170
|
+
if (env.COS_LIVE_CUES_GROUNDING === 'classic') return { value: 'classic', source: 'env', locked: true }
|
|
171
|
+
let stored: LiveCuesGrounding | null = null
|
|
172
|
+
try { const v = settings?.grounding?.(); stored = isGrounding(v) ? v : null } catch { stored = null }
|
|
173
|
+
return stored !== null ? { value: stored, source: 'setting', locked: false } : { value: 'classic', source: 'default', locked: false }
|
|
174
|
+
}
|
|
175
|
+
|
|
97
176
|
let singleton: LiveCuesV2Settings | null = null
|
|
98
177
|
export function liveCuesV2Settings(): LiveCuesV2Settings {
|
|
99
178
|
if (!singleton) singleton = new LiveCuesV2Settings(dataPath('live-cues-v2-settings.json'))
|