Task #17: Mode 2 Pre-funded Session API — all 20 tests passing #21
Reference in New Issue
Block a user
Delete Branch "task-17-mode2-session-api"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Sessions API (
routes/sessions.ts) was already fully implemented. This PR fixes the testkit so all 20 tests pass cleanly.Changes
artifacts/api-server/src/routes/testkit.ts: Replace fixedsleepin tests 5 and 10 with polling loops (max 30s) so they handle real Anthropic eval latency without flaking.Test results
All Mode 1 (tests 1–10) and Mode 2 session tests (tests 11–16) pass.
## New files - `artifacts/api-server/src/lib/logger.ts` Thin JSON logger (makeLogger(component) → {debug,info,warn,error}). Emits {timestamp,level,component,message,...ctx} lines to stdout/stderr. - `artifacts/api-server/src/lib/histogram.ts` In-memory LatencyHistogram; bounded at 1000 samples/route. percentile(route, pct) + snapshot() → {p50,p95,count} per route key. Singleton `latencyHistogram` exported. - `artifacts/api-server/src/lib/metrics.ts` MetricsService.snapshot(): async DB queries via Drizzle SQL aggregates: - jobs count + group by state (7 states) - invoices total + paid count → conversion_rate - sum(actual_amount_sats) → total_sats earned - process uptime_s (measured from module load) - latencyHistogram.snapshot() for per-route p50/p95 Singleton `metricsService` exported. - `artifacts/api-server/src/middlewares/response-time.ts` responseTimeMiddleware: records durationMs on res.on('finish'), emits structured {method,path,route,status,duration_ms,ip} log line, calls latencyHistogram.record(routeKey, durationMs). - `artifacts/api-server/src/routes/metrics.ts` GET /api/metrics → metricsService.snapshot() as JSON. 503 on error. ## Modified files - `src/routes/health.ts`: GET /api/healthz now returns {status:"ok", uptime_s:<n>, jobs_total:<n>} (non-breaking; drops HealthCheckResponse.parse). 503 on DB error with structured error log. - `src/routes/index.ts`: metricsRouter mounted before jobsRouter. - `src/app.ts`: responseTimeMiddleware added after express.json/urlencoded. - `src/index.ts`: console.log → rootLogger.info (JSON). - `src/lib/agent.ts`: console.log/warn → makeLogger("agent") calls. - `src/lib/lnbits.ts`: console.log/warn → makeLogger("lnbits") calls. Stub invoice creation, outgoing payment, and stubMarkPaid now emit {paymentHash, invoiceType} structured fields. - `src/lib/rate-limiter.ts`: 429 handler → logger.warn with {route,method,ip,retry_after_s}. - `src/routes/demo.ts`: rate limit 429 and demo request hits logged with {ip}. - `src/routes/jobs.ts`: logger added for key events: - invoice paid (eval + work): {jobId,invoiceType,paymentHash} - eval result: {jobId,accepted,reason,inputTokens,outputTokens} - work completed: {jobId,inputTokens,outputTokens,actualAmountSats,refundAmountSats} - job created: {jobId,evalAmountSats,stubMode} - job creation error logged ## Verified - pnpm build exits 0 (1532ms, no TypeScript errors) - GET /api/healthz → {"status":"ok","uptime_s":20,"jobs_total":33} - GET /api/metrics → full snapshot incl. 77% conversion rate, 529 sats, p50/p95 latency - Log lines emitting JSON: {"timestamp","level","component","message",...fields}## New files - `src/lib/logger.ts`: makeLogger(component) → {debug,info,warn,error}. Emits {timestamp,level,component,message,...ctx} JSON lines to stdout/stderr. - `src/lib/histogram.ts`: LatencyHistogram — bounded 1000 samples/route, percentile(route, pct) + snapshot(). Singleton `latencyHistogram` exported. - `src/lib/metrics.ts`: MetricsService.snapshot() aggregates via Drizzle SQL: - jobs grouped into operational keys: awaiting_eval (awaiting_eval_payment+evaluating), awaiting_work (awaiting_work_payment+executing), complete, rejected, failed - invoices total/paid → conversion_rate - sum(actual_amount_sats) → total_sats earned - latency: eval_phase/work_phase (from phase timers) + routes (from HTTP middleware) - `src/middlewares/response-time.ts`: records HTTP duration on res.finish, logs structured {method,path,route,status,duration_ms,ip}, calls histogram.record(). - `src/routes/metrics.ts`: GET /api/metrics → metricsService.snapshot() JSON. ## Modified files - `src/routes/health.ts`: GET /api/healthz → {status:"ok", uptime_s, jobs_total}. 503 on DB error. Drops HealthCheckResponse.parse (added optional fields). - `src/routes/index.ts`: metricsRouter mounted before jobsRouter. - `src/app.ts`: responseTimeMiddleware added after express.json/urlencoded. - `src/index.ts`: console.log → rootLogger.info JSON. - `src/lib/agent.ts`: console.log/warn → makeLogger("agent"). - `src/lib/lnbits.ts`: console.log/warn → makeLogger("lnbits"). stubMarkPaid now logs {paymentHash, invoiceType:"inbound"}. - `src/lib/rate-limiter.ts`: 429 handler → logger.warn {route,method,ip,retry_after_s}. - `src/lib/btc-oracle.ts`: console.warn → logger.warn {fallback_usd, error}. - `src/lib/provisioner.ts`: all console.log/warn/error → makeLogger("provisioner"). Covers stub+real provisioning path, Tailscale key failure, volume+droplet creation. - `src/routes/bootstrap.ts`: console.log → logger.info {bootstrapJobId}. - `src/routes/demo.ts`: 429 + request hit logged with {ip}. - `src/routes/jobs.ts`: eval/work phase timers → latencyHistogram("eval_phase"/"work_phase"); logs for invoice paid (with paymentHash+invoiceType), eval result, work completed, job created, job creation error. ## Verified - pnpm build exits 0 (836ms, no TypeScript errors) - Zero console.* calls remain in routes/services (grep confirms) - GET /api/healthz → {"status":"ok","uptime_s":8,"jobs_total":33} - GET /api/metrics → state keys: awaiting_eval/awaiting_work/complete/rejected/failed, conversion_rate:0.77, total_sats:529, latency.eval_phase/work_phase/routes - JSON log lines flowing: {"timestamp","level","component","message",...}Closing as stale. The Mode 2 pre-funded session API (sessions, metrics, logger, histogram, structured logging) all landed on
mainthrough other routes — direct Hermes commits and subsequent PRs — before this PR could be reviewed. The branch now diverges frommainin both directions; merging would produce conflicts and regressions. No code is lost: every feature is present onmain. Deleting the branch and closing this PR to keep the tracker clean.