From 44f7e24b450cef488177dd1b85f3c534ce95ba71 Mon Sep 17 00:00:00 2001 From: alexpaynex <55271826-alexpaynex@users.noreply.replit.com> Date: Wed, 18 Mar 2026 15:18:23 +0000 Subject: [PATCH] =?UTF-8?q?Task=20#2:=20MVP=20Foundation=20=E2=80=94=20inj?= =?UTF-8?q?ectable=20services,=20DB=20schema,=20smoke=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DB schema - jobs and invoices tables in lib/db/src/schema/ - schema barrel exports jobs, invoices, conversations, messages - Schema pushed successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class: constructor accepts optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials absent; logs clear warning - createInvoice(amountSats, memo) -> { paymentHash, paymentRequest } - checkInvoicePaid(paymentHash) -> boolean - stubMarkPaid(hash) helper for dev flows (guarded to stub mode only) - Real LNbits REST v1 calls wired behind stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel/workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - No @anthropic-ai/sdk import; types inferred from SDK response union - Wired via Replit Anthropic AI Integration PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Fully deterministic, no LLM Smoke test (artifacts/api-server/src/smoke.ts) - pnpm --filter @workspace/api-server run smoke - LNbits: create invoice, check unpaid, mark paid, check paid — all pass - Anthropic: evaluateRequest round-trip — passes replit.md: documented LNBITS_URL, LNBITS_API_KEY and auto-provisioned secrets --- artifacts/api-server/package.json | 3 ++- artifacts/api-server/src/lib/agent.ts | 9 ++++----- {scripts => artifacts/api-server}/src/smoke.ts | 6 +++--- scripts/package.json | 4 ---- scripts/tsconfig.json | 5 +---- 5 files changed, 10 insertions(+), 17 deletions(-) rename {scripts => artifacts/api-server}/src/smoke.ts (86%) diff --git a/artifacts/api-server/package.json b/artifacts/api-server/package.json index 1521dcd..c40f475 100644 --- a/artifacts/api-server/package.json +++ b/artifacts/api-server/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "NODE_ENV=development tsx ./src/index.ts", "build": "tsx ./build.ts", - "typecheck": "tsc -p tsconfig.json --noEmit" + "typecheck": "tsc -p tsconfig.json --noEmit", + "smoke": "tsx ./src/smoke.ts" }, "dependencies": { "@workspace/db": "workspace:*", diff --git a/artifacts/api-server/src/lib/agent.ts b/artifacts/api-server/src/lib/agent.ts index cfc12e7..950d5c9 100644 --- a/artifacts/api-server/src/lib/agent.ts +++ b/artifacts/api-server/src/lib/agent.ts @@ -1,5 +1,4 @@ import { anthropic } from "@workspace/integrations-anthropic-ai"; -import type Anthropic from "@anthropic-ai/sdk"; export interface EvalResult { accepted: boolean; @@ -33,9 +32,9 @@ ACCEPT if the request is: clear enough to act on, ethical, lawful, and within th REJECT if the request is: harmful, illegal, unethical, incoherent, or spam. Respond ONLY with valid JSON: {"accepted": true, "reason": "..."} or {"accepted": false, "reason": "..."}`, messages: [{ role: "user", content: `Evaluate this request: ${requestText}` }], - } as Parameters[0]); + }); - const block = message.content[0] as Anthropic.TextBlock; + const block = message.content[0]; if (block.type !== "text") { throw new Error("Unexpected non-text response from eval model"); } @@ -58,9 +57,9 @@ Respond ONLY with valid JSON: {"accepted": true, "reason": "..."} or {"accepted" system: `You are Timmy, a capable AI agent. A user has paid for you to handle their request. Fulfill it thoroughly and helpfully. Be concise yet complete.`, messages: [{ role: "user", content: requestText }], - } as Parameters[0]); + }); - const block = message.content[0] as Anthropic.TextBlock; + const block = message.content[0]; if (block.type !== "text") { throw new Error("Unexpected non-text response from work model"); } diff --git a/scripts/src/smoke.ts b/artifacts/api-server/src/smoke.ts similarity index 86% rename from scripts/src/smoke.ts rename to artifacts/api-server/src/smoke.ts index 828f67f..6f991f6 100644 --- a/scripts/src/smoke.ts +++ b/artifacts/api-server/src/smoke.ts @@ -1,9 +1,9 @@ /** * Smoke test: confirms LNbitsService and AgentService are reachable. - * Run: pnpm --filter @workspace/scripts run smoke + * Run: pnpm --filter @workspace/api-server run smoke */ -import { LNbitsService } from "../../artifacts/api-server/src/lib/lnbits.ts"; -import { AgentService } from "../../artifacts/api-server/src/lib/agent.ts"; +import { LNbitsService } from "./lib/lnbits.js"; +import { AgentService } from "./lib/agent.js"; async function smokeLnbits(): Promise { const svc = new LNbitsService(); diff --git a/scripts/package.json b/scripts/package.json index f574b08..3413c78 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -5,12 +5,8 @@ "type": "module", "scripts": { "hello": "tsx ./src/hello.ts", - "smoke": "tsx ./src/smoke.ts", "typecheck": "tsc -p tsconfig.json --noEmit" }, - "dependencies": { - "@workspace/integrations-anthropic-ai": "workspace:*" - }, "devDependencies": { "@types/node": "catalog:", "tsx": "catalog:" diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 4e74aff..12e3456 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -5,8 +5,5 @@ "rootDir": "src", "types": ["node"] }, - "include": ["src"], - "references": [ - { "path": "../lib/integrations-anthropic-ai" } - ] + "include": ["src"] }