···6969}
70707171/**
7272+ * Get a fully authenticated agent for the session.
7373+ * In production: wraps the OAuth session.
7474+ * In dev-full mode (no oauthSession): logs in with app password.
7575+ * Use this in orchestrators instead of createAgent().
7676+ */
7777+export async function getAgent(session: Session): Promise<Agent> {
7878+ if (session.oauthSession) {
7979+ return new Agent(session.oauthSession);
8080+ }
8181+ return loginDevAgent(session);
8282+}
8383+8484+/**
7285 * Create and log in a dev-mode agent. Use this instead of createAgent()
7386 * when in dev mode (session.oauthSession is undefined).
7487 */
+8-4
src/firehose/index.ts
···2233import { IdResolver } from "@atproto/identity";
44import { type Event, Firehose, MemoryRunner } from "@atproto/sync";
55-import { getRelayUrl } from "../atproto/env.ts";
55+import { getDevPdsUrl, getDevPlcUrl, getRelayUrl } from "../atproto/env.ts";
66import { COLLECTIONS } from "../lib/constants.ts";
77import { getCursor, setCursor } from "../server/db/queries/index.ts";
88import { handleCommitEvent } from "./handlers.ts";
991010const relayUrl = getRelayUrl();
1111-const savedCursor = getCursor();
1111+// In dev mode the PDS is ephemeral — each run starts fresh from seq 0.
1212+// Persisting the cursor across sessions causes "FutureCursor" errors.
1313+const isDevMode = !!getDevPdsUrl();
1414+const savedCursor = isDevMode ? null : getCursor();
12151316console.log(`Firehose connecting to ${relayUrl}`);
1417if (savedCursor) {
1518 console.log(`Resuming from cursor ${savedCursor}`);
1619}
17201818-const idResolver = new IdResolver();
2121+const plcUrl = getDevPlcUrl() ?? undefined;
2222+const idResolver = new IdResolver({ plcUrl });
19232024const runner = new MemoryRunner({
2125 startCursor: savedCursor ?? undefined,
2226 setCursor: async (cursor: number) => {
2323- setCursor(cursor);
2727+ if (!isDevMode) setCursor(cursor);
2428 },
2529});
2630