@gotcos/glasses-server 6.43.4 → 6.44.1

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.
@@ -106,8 +106,12 @@ export interface QueryJobRequest {
106
106
  * (see `FINGERPRINT_KEYS`), so a rollback to a build that drops it still
107
107
  * hydrates the journal. */
108
108
  origin?: QueryJobOrigin
109
+ /** Read-only dispatch constraint. Excluded from the fingerprint. */
110
+ dispatch?: { restricted: true; tools: readonly string[] }
109
111
  }
110
112
 
113
+ export const DISPATCH_ALLOWED_TOOLS = ['Read', 'Grep', 'Glob'] as const
114
+
111
115
  export interface QueryJobProviderLinkage {
112
116
  provider?: 'claude' | 'codex' | 'cursor' | 'ollama'
113
117
  resolvedModel?: string
@@ -214,6 +218,8 @@ export interface QueryJobStoreHealth {
214
218
  /** Requests whose `origin` was present but not one this build recognises
215
219
  * (a bare string, or an object of a later build's kind) and was dropped. */
216
220
  originDropped: number
221
+ /** Public-route admissions that had `dispatch` or `origin.kind==='task'` stripped. */
222
+ originStripped: number
217
223
  /** Journal `accepted` records whose stored fingerprint no longer matches the
218
224
  * running build's `requestFingerprint` — each one is a job that did NOT
219
225
  * hydrate. Zero is the only healthy value. */
@@ -225,6 +231,10 @@ export interface QueryJobStoreHealth {
225
231
  }
226
232
 
227
233
  const CLIENT_JOB_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
234
+
235
+ export function isClientJobId(value: string): boolean {
236
+ return CLIENT_JOB_ID_RE.test(value.toLowerCase())
237
+ }
228
238
  const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:@/-]*$/
229
239
  const CONTROL_RE = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g
230
240
  const SECRET_PATTERNS: RegExp[] = [
@@ -336,6 +346,11 @@ export function parseQueryJobRequest(raw: unknown, options: ParseQueryJobRequest
336
346
  // counted by the store), or a newer client would have every prompt 400'd.
337
347
  }
338
348
 
349
+ const dispatch = parseDispatch(input.dispatch)
350
+ if (options.strictOrigin && origin?.kind === 'task' && !dispatch) {
351
+ throw new QueryJobValidationError('invalid_dispatch')
352
+ }
353
+
339
354
  const attachmentIds = parseMediaIdList(input.attachmentIds)
340
355
  const attachmentRefs = parseMediaAttachmentRefs(input.attachmentRefs ?? input.attachments)
341
356
  if (!query.trim() && attachmentIds.length === 0 && attachmentRefs.length === 0) {
@@ -360,7 +375,22 @@ export function parseQueryJobRequest(raw: unknown, options: ParseQueryJobRequest
360
375
  attachmentRefs,
361
376
  activityToolMode,
362
377
  ...(origin ? { origin } : {}),
378
+ ...(dispatch ? { dispatch } : {}),
379
+ }
380
+ }
381
+
382
+ function parseDispatch(raw: unknown): QueryJobRequest['dispatch'] | undefined {
383
+ if (raw == null) return undefined
384
+ if (!raw || typeof raw !== 'object') throw new QueryJobValidationError('invalid_dispatch')
385
+ const rec = raw as Record<string, unknown>
386
+ if (rec.restricted !== true || !Array.isArray(rec.tools) || rec.tools.length === 0) {
387
+ throw new QueryJobValidationError('invalid_dispatch')
388
+ }
389
+ const tools = rec.tools.map(String)
390
+ if (tools.some(tool => !(DISPATCH_ALLOWED_TOOLS as readonly string[]).includes(tool))) {
391
+ throw new QueryJobValidationError('invalid_dispatch')
363
392
  }
393
+ return { restricted: true, tools }
364
394
  }
365
395
 
366
396
  /** True when the raw admission/journal input carried an `origin` the parser
@@ -410,7 +440,7 @@ export const FINGERPRINT_KEYS = [
410
440
  /** The request keys deliberately OUTSIDE the identity. Every key of
411
441
  * `QueryJobRequest` must appear in exactly one of the two lists: adding a key
412
442
  * without naming it here or above fails to compile, so the author picks a side. */
413
- export const FINGERPRINT_EXCLUDED = ['origin'] as const satisfies readonly (keyof QueryJobRequest)[]
443
+ export const FINGERPRINT_EXCLUDED = ['origin', 'dispatch'] as const satisfies readonly (keyof QueryJobRequest)[]
414
444
  type FingerprintUncovered = Exclude<keyof QueryJobRequest, typeof FINGERPRINT_KEYS[number] | typeof FINGERPRINT_EXCLUDED[number]>
415
445
  export const FINGERPRINT_KEYS_COVER_REQUEST: FingerprintUncovered extends never ? true : never = true
416
446