mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
in push-to-talk, answer a Gemini tool call that arrives with no input, reply or tool result outstanding with a failure instead of running it, and refuse every other call in the reply it starts, including one after unprompted speech; the reply stays tracked so the next input retires it and its late boundary cannot close the user's turn in push-to-talk, a caption that arrives after its turn ended no longer opens a reply, so the next request is not retired with its tool calls discarded open mic cannot tell either case from a new reply and stays unchanged check packet ownership after the narration callback before queuing tools, and raise the per-connection call history to 4096 IDs move the pointer turn event, context item and announcement into pointerContext so the OpenAI and Gemini controllers share them, and drop the Gemini token route's copy of the shared exact-Origin comparison
80 lines
2.3 KiB
JavaScript
80 lines
2.3 KiB
JavaScript
import { readRequestBody } from '../common/request.js';
|
|
import { admitSameSiteRequest } from '../../../src/localRequestGate.mjs';
|
|
import {
|
|
isAllowedHost,
|
|
resolveAllowedHosts,
|
|
} from '../../../build/allowedHosts.js';
|
|
|
|
/** Use the app's Host and request policies, with a required browser Origin. */
|
|
export function isGeminiRequestOriginAllowed(
|
|
req,
|
|
configuredHost = '',
|
|
configuredAllowedHosts,
|
|
) {
|
|
const host = req.headers?.host;
|
|
const origin = req.headers?.origin;
|
|
if (
|
|
typeof host !== 'string' ||
|
|
typeof origin !== 'string' ||
|
|
!host ||
|
|
!origin ||
|
|
/[\s/?#@\\]/.test(host)
|
|
)
|
|
return false;
|
|
const protocol = req.socket?.encrypted ? 'https:' : 'http:';
|
|
if (
|
|
!admitSameSiteRequest({
|
|
hostHeader: host,
|
|
protocol,
|
|
origin,
|
|
secFetchSite: req.headers?.['sec-fetch-site'],
|
|
proxyHeaders: req.headers || {},
|
|
}).ok
|
|
)
|
|
return false;
|
|
// The shared gate compares a present Origin exactly with this authority;
|
|
// this route also requires the Origin and an allowed Host.
|
|
const boundHost = String(configuredHost || '')
|
|
.trim()
|
|
.toLowerCase();
|
|
return isAllowedHost(host, resolveAllowedHosts(configuredAllowedHosts), [
|
|
boundHost,
|
|
]);
|
|
}
|
|
|
|
/** Admit only the existing input gesture; callers cannot supply model/setup. */
|
|
export async function readGeminiTokenRequest(req, signal, maxBytes = 1024) {
|
|
const declared = Number(req.headers?.['content-length']);
|
|
const fail = (code) =>
|
|
Object.assign(new Error('Invalid token request'), { code });
|
|
const body = await readRequestBody(
|
|
req,
|
|
maxBytes,
|
|
Number.isFinite(declared) && declared > maxBytes
|
|
? AbortSignal.abort(fail('BODY_TOO_LARGE'))
|
|
: signal,
|
|
);
|
|
if (!body.trim()) return 'open-mic';
|
|
if (
|
|
!/^application\/json(?:\s*;|$)/i.test(req.headers?.['content-type'] || '')
|
|
)
|
|
throw fail('BAD_CONTENT_TYPE');
|
|
let parsed;
|
|
try {
|
|
parsed = JSON.parse(body);
|
|
} catch {
|
|
throw fail('BAD_BODY');
|
|
}
|
|
if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object')
|
|
throw fail('BAD_BODY');
|
|
const keys = Object.keys(parsed);
|
|
if (!keys.length) return 'open-mic';
|
|
if (
|
|
keys.length === 1 &&
|
|
keys[0] === 'inputMode' &&
|
|
parsed.inputMode === 'push-to-talk'
|
|
)
|
|
return 'push-to-talk';
|
|
throw fail('BAD_BODY');
|
|
}
|