Add session mode for pre-funded request processing

Implement session-based API endpoints for creating, managing, and interacting with pre-funded sessions, including deposit and top-up invoice generation, macaroon authentication, and per-request debiting of compute costs.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 2dc3847e-7186-4a22-9c7e-16cd31bca8d9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/sPDHkg8
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
alexpaynex
2026-03-18 20:00:24 +00:00
parent dfc9ecdc7b
commit ab2cc06a79
29 changed files with 1075 additions and 978 deletions

View File

@@ -3,3 +3,4 @@ export * from "./invoices";
export * from "./conversations";
export * from "./messages";
export * from "./bootstrap-jobs";
export * from "./sessions";

View File

@@ -0,0 +1,101 @@
import { pgTable, text, timestamp, integer, boolean, real } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod/v4";
// ── Session state machine ─────────────────────────────────────────────────────
export const SESSION_STATES = [
"awaiting_payment",
"active",
"paused",
"expired",
] as const;
export type SessionState = (typeof SESSION_STATES)[number];
export const SESSION_REQUEST_STATES = [
"processing",
"complete",
"rejected",
"failed",
] as const;
export type SessionRequestState = (typeof SESSION_REQUEST_STATES)[number];
// ── sessions ──────────────────────────────────────────────────────────────────
export const sessions = pgTable("sessions", {
id: text("id").primaryKey(),
state: text("state").$type<SessionState>().notNull().default("awaiting_payment"),
balanceSats: integer("balance_sats").notNull().default(0),
depositAmountSats: integer("deposit_amount_sats").notNull(),
// Deposit invoice (stored inline — avoids FK into jobs table)
depositPaymentHash: text("deposit_payment_hash").notNull(),
depositPaymentRequest: text("deposit_payment_request").notNull(),
depositPaid: boolean("deposit_paid").notNull().default(false),
// Current topup invoice (one at a time; all nullable until created)
topupAmountSats: integer("topup_amount_sats"),
topupPaymentHash: text("topup_payment_hash"),
topupPaymentRequest: text("topup_payment_request"),
topupPaid: boolean("topup_paid"),
// Auth token — issued once when session activates; required for requests
macaroon: text("macaroon"),
// TTL — refreshed on each successful request
expiresAt: timestamp("expires_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
});
export const insertSessionSchema = createInsertSchema(sessions).omit({
createdAt: true,
updatedAt: true,
});
export type Session = typeof sessions.$inferSelect;
export type InsertSession = z.infer<typeof insertSessionSchema>;
// ── session_requests ──────────────────────────────────────────────────────────
export const sessionRequests = pgTable("session_requests", {
id: text("id").primaryKey(),
sessionId: text("session_id")
.notNull()
.references(() => sessions.id),
request: text("request").notNull(),
state: text("state")
.$type<SessionRequestState>()
.notNull()
.default("processing"),
result: text("result"),
reason: text("reason"),
errorMessage: text("error_message"),
// Eval token usage (Haiku judge)
evalInputTokens: integer("eval_input_tokens"),
evalOutputTokens: integer("eval_output_tokens"),
// Work token usage (Sonnet; null for rejected requests)
workInputTokens: integer("work_input_tokens"),
workOutputTokens: integer("work_output_tokens"),
// Balance debit for this request
debitedSats: integer("debited_sats"),
balanceAfterSats: integer("balance_after_sats"),
btcPriceUsd: real("btc_price_usd"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
});
export const insertSessionRequestSchema = createInsertSchema(sessionRequests).omit({
createdAt: true,
updatedAt: true,
});
export type SessionRequest = typeof sessionRequests.$inferSelect;
export type InsertSessionRequest = z.infer<typeof insertSessionRequestSchema>;