From 082375ed295e393dd03a9c4065110f47c0ab70cb Mon Sep 17 00:00:00 2001 From: DIodide Date: Mon, 28 Sep 2026 13:50:10 -0400 Subject: [PATCH] TigerInbox connector: drop stuck failed/OAuth connections; log token fingerprint A connection whose token was once rejected stayed in the agents SDK's 'failed' (or OAuth 'authenticating') state for the life of the Durable Object, so the connector kept failing even after the secret was corrected. Bearer-token apps now reconnect fresh when their stored connection is failed or authenticating. --- app/src/server/pi.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/server/pi.ts b/app/src/server/pi.ts index 21e1dd0..6399ee1 100644 --- a/app/src/server/pi.ts +++ b/app/src/server/pi.ts @@ -366,6 +366,12 @@ export class Pi extends Think { !enabled.has(app.key) || (identityChanged && app.key !== "gcal") || (app.key === "gcal" && !gcalEntitled) || + // Bearer-token apps never need OAuth: a stored connection that failed or + // fell into an OAuth flow (e.g. after a rejected token) would otherwise + // stay stuck in that state for the life of the object. Drop it and + // reconnect fresh with the current credentials. + (app.key !== "gcal" && + (server.state === "failed" || server.state === "authenticating")) || server.server_url !== expectedUrl(app); if (stale) await this.removeMcpServer(id); } @@ -466,6 +472,7 @@ export class Pi extends Think { private connectFailure(app: PiApp, err: unknown): string { const raw = err instanceof Error ? err.message : String(err); console.warn(`connect ${app.key} failed:`, raw); + if (app.key === "tigerinbox") void this.logTokenFingerprint(); const text = raw.toLowerCase(); const has = (...needles: string[]) => needles.some((n) => text.includes(n)); if (has("timeout", "timed out", "abort")) { @@ -875,6 +882,17 @@ export class Pi extends Think { return (this.env.ENGINE_MCP_BASE || DEFAULT_ENGINE_BASE).replace(/\/$/, ""); } + /** + * Log a short SHA-256 fingerprint (never the value) of the configured + * TigerInbox token, so a mismatch with TigerInbox's own token is provable. + */ + private async logTokenFingerprint(): Promise { + const token = this.env.TIGERINBOX_MCP_TOKEN ?? ""; + const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token)); + const hex = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join(""); + console.warn(`tigerinbox token fingerprint: ${token ? hex.slice(0, 12) : "(empty)"}`); + } + /** * TigerInbox's connector reads a shared, public-listserv archive, so it takes * PI's own token and deliberately no student identity headers.