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:
@@ -3,3 +3,4 @@ export * from "./invoices";
|
||||
export * from "./conversations";
|
||||
export * from "./messages";
|
||||
export * from "./bootstrap-jobs";
|
||||
export * from "./sessions";
|
||||
|
||||
101
lib/db/src/schema/sessions.ts
Normal file
101
lib/db/src/schema/sessions.ts
Normal 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>;
|
||||
Reference in New Issue
Block a user