@1presence/bridge 0.64.0 → 0.66.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/dist/auth.js +14 -3
- package/dist/index.js +16 -0
- package/package.json +1 -1
package/dist/auth.js
CHANGED
|
@@ -197,11 +197,22 @@ async function refreshIdToken(refreshToken) {
|
|
|
197
197
|
throw new Error('Token refresh returned no id_token');
|
|
198
198
|
return data.id_token;
|
|
199
199
|
}
|
|
200
|
-
/**
|
|
200
|
+
/** Headroom below which ensureFreshToken mints a new ID token at turn start.
|
|
201
|
+
* Firebase ID tokens live 60 min; the token is baked into the MCP SSE config
|
|
202
|
+
* header at spawn (writeMcpConfig) and reused by that connection for the whole
|
|
203
|
+
* turn — it can't be re-tokened mid-turn without reconnecting the MCP server.
|
|
204
|
+
* So a turn must START with enough headroom to outlive itself. At 10 min a turn
|
|
205
|
+
* that began with ~12 min of life and ran 17 min lost its MCP OAuth AND its
|
|
206
|
+
* save-turn token mid-flight ("MCP OAuth not configured" + save-turn 401 — see
|
|
207
|
+
* the 2026-07-07 stuck-run bug). 30 min comfortably covers the 10-min bridge
|
|
208
|
+
* stage wall-clock and any normal chat turn; near-full-hour refresh would churn
|
|
209
|
+
* a token every turn for little gain. */
|
|
210
|
+
const TOKEN_REFRESH_HEADROOM_SEC = 30 * 60;
|
|
211
|
+
/** Returns auth with a fresh ID token. Refreshes in-memory if less than
|
|
212
|
+
* TOKEN_REFRESH_HEADROOM_SEC of validity remains. */
|
|
201
213
|
export async function ensureFreshToken(auth) {
|
|
202
214
|
const { exp } = parseJwt(auth.token);
|
|
203
|
-
|
|
204
|
-
if (exp && exp > Math.floor(Date.now() / 1000) + tenMinutes)
|
|
215
|
+
if (exp && exp > Math.floor(Date.now() / 1000) + TOKEN_REFRESH_HEADROOM_SEC)
|
|
205
216
|
return auth;
|
|
206
217
|
if (!auth.refreshToken)
|
|
207
218
|
return auth; // no refresh token, use as-is
|
package/dist/index.js
CHANGED
|
@@ -433,6 +433,22 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
|
|
|
433
433
|
catch (err) {
|
|
434
434
|
console.warn(`[bridge] spool write failed: ${err.message}`);
|
|
435
435
|
}
|
|
436
|
+
// Refresh the token right before the POST — this fires at turn END, when the
|
|
437
|
+
// most time has elapsed. A long turn (started with a token that was fresh
|
|
438
|
+
// then) must not 401 on save-turn with a since-expired token — the failure
|
|
439
|
+
// that left a run stuck at 'running' on 2026-07-07. Best-effort: on refresh
|
|
440
|
+
// failure fall back to the current token; the spool file is retried on the
|
|
441
|
+
// next successful POST or on the next bridge startup.
|
|
442
|
+
try {
|
|
443
|
+
const fresh = await ensureFreshToken(activeAuth);
|
|
444
|
+
if (fresh.token !== activeAuth.token) {
|
|
445
|
+
activeAuth = fresh;
|
|
446
|
+
currentAuth = fresh;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
console.warn(`[bridge] pre-save-turn token refresh failed (using current token): ${err.message}`);
|
|
451
|
+
}
|
|
436
452
|
const result = await postSaveTurn(GATEWAY_HTTP, activeAuth.token, record);
|
|
437
453
|
if (result.ok) {
|
|
438
454
|
deleteSpool(record.conversationId);
|