237 lines
12 KiB
Markdown
237 lines
12 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to wpide-server will be documented in this file. Format
|
||
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning
|
||
is [SemVer](https://semver.org/spec/v2.0.0.html).
|
||
|
||
## [0.6.3] — 2026-05-26
|
||
|
||
### Changed
|
||
- **Request logging back to `!isDev` (silent in prod).** Was temporarily
|
||
forced on to debug the live browser-direct tool round-trip with the
|
||
plugin on Hostinger (see plugin 56.95/56.96). Now that browser-direct
|
||
is verified end-to-end (744 ms tool round-trip from `mersaai.diretenders.com`),
|
||
prod logs go quiet again — every 30s `/v1/health` from Docker's
|
||
healthcheck no longer fills the log file.
|
||
|
||
### Kept
|
||
- The targeted diagnostic lines added in 0.6.2 stay in for future
|
||
support work:
|
||
- `browser-direct: awaiting tool result from browser` (orchestrator)
|
||
- `browser-direct: tool result TIMED OUT after 90s` (orchestrator)
|
||
- `tool_result POST received … matched:true|false` (runs route)
|
||
These are low-volume and useful for diagnosing future host quirks.
|
||
|
||
## [0.6.2] — 2026-05-26
|
||
|
||
### Fixed
|
||
- **`z.coerce.boolean()` read every boolean env var as `true`.** `Boolean("false")`
|
||
is `true` in JS, so `ALLOW_INSECURE_TLS=false`, `REQUIRE_LICENSE=false`, and
|
||
`FREE_TIER_ACTIVE=false` were all silently `true`. Replaced with a `boolish()`
|
||
parser that treats `0/false/no/off` as false and `1/true/yes/on` as true.
|
||
This bit on the first real VPS deploy: TLS verification stayed disabled and
|
||
license gating would have switched on unexpectedly.
|
||
|
||
### Added
|
||
- **Production deploy artifacts (`deploy/`).** `vps-bootstrap.sh` (Ubuntu prep:
|
||
update, ufw, base tools) and `docker-compose.coolify.yml` (runs the container
|
||
behind Coolify's existing Traefik proxy on the external `coolify` network, with
|
||
Let's Encrypt labels). First live deploy: Contabo VPS → Coolify/Traefik →
|
||
`https://api.qbirr.com`.
|
||
|
||
## [0.6.1] — 2026-05-26
|
||
|
||
### Fixed
|
||
- **SSE stream endpoint now sends CORS headers.** The `/v1/runs/:id/stream`
|
||
reply is hijacked (raw), so the cors plugin didn't run on it. Added
|
||
`access-control-allow-origin` (echoes request origin) so the plugin's
|
||
browser-direct `EventSource` can open the stream cross-origin (browser
|
||
on the WP site → server on another domain). Without this the browser
|
||
blocked the stream.
|
||
|
||
## [0.6.0] — 2026-05-25 — Memory, teams, browser-direct tools
|
||
|
||
### Added
|
||
- **Server-side conversation memory (`orchestrator/memory.ts`).** Persists
|
||
each run's user goal + final answer per `session_id`; recalls the last
|
||
~12 turns and merges (de-duplicated) into context on the next run. Runs
|
||
now have continuity even when the plugin sends little history. (DB
|
||
migration v3: `conversation_turns`.)
|
||
- **Multi-tenant teams (`accounts/teams.ts`, `routes/teams.ts`).** Orgs +
|
||
members + roles; `POST/GET /v1/teams`, member add/remove. A member
|
||
**inherits the org owner's subscription tier** (team owner pays once,
|
||
seats share the tier) — wired into `resolveAccess`. (Migration v3:
|
||
`orgs`, `org_members`, `subscriptions.org_id`.)
|
||
- **Browser-direct tool execution (cap-immune transport, server side).**
|
||
`browser_tools: true` on a run makes the loop emit `tool_call` SSE
|
||
events and await `POST /v1/runs/:id/tool_result` from the browser,
|
||
instead of calling back into the plugin. Removes the long-lived request
|
||
from the WP host entirely → works on any shared host. Relay mode
|
||
(plugin callback) remains the default; this is additive. Proven
|
||
end-to-end via test (tool_call emitted → result posted → run completed
|
||
using the posted data).
|
||
|
||
### Notes
|
||
- The browser-direct **plugin JS** (browser opens EventSource + runs the
|
||
tool via admin-ajax + POSTs the result) is the remaining client-side
|
||
step; the server fully supports it now.
|
||
|
||
## [0.5.0] — 2026-05-25 — SaaS platform foundation
|
||
|
||
### Added
|
||
- **Accounts & auth.** Email+password (scrypt, no native deps), Google +
|
||
GitHub OAuth (authorization-code flow), session JWT in an httpOnly
|
||
cookie. `/v1/auth/{register,login,logout,me,set-password}` and
|
||
`/v1/auth/oauth/:provider/{start,callback}`.
|
||
- **Licensing & subscriptions (no credits).** Each account gets a license
|
||
key + a subscription row (tier `basic|pro|max`, status). The run path
|
||
resolves the license → tier and gates on an active subscription
|
||
(`REQUIRE_LICENSE`, default off in dev so local testing needs no
|
||
account; `DEV_DEFAULT_TIER` applies when off).
|
||
- **Model routing by tier** (`routing/policy.ts`): tier sets the model
|
||
ceiling — basic→flash, pro→+thinking, max→+pro-max — combined with
|
||
mode/complexity. Explicit model picks are honored only within the tier,
|
||
else downgraded. DeepSeek `thinking` param wired through.
|
||
- **Stripe billing** (`fetch`, no SDK): `/v1/billing/{checkout,portal,webhook}`
|
||
with manual webhook signature verification; subscription state mirrored
|
||
into the DB. Disabled cleanly when keys absent.
|
||
- **Dashboard** at `/app`: signup/login (email + Google + GitHub),
|
||
license key with copy, plan + tier picker + manage-billing. Single
|
||
inline HTML page, no build step.
|
||
- **DB migration v2:** users, oauth_identities, licenses, subscriptions,
|
||
sites (site registry); runs gain user_id/tier/model.
|
||
- **Deploy:** `SETUP.md` operator checklist, `docker-compose.prod.yml` +
|
||
`Caddyfile.prod` for a plain VPS (Coolify uses the Dockerfile directly).
|
||
SQLite on a persistent volume for v1; Postgres is the documented
|
||
scale-up path.
|
||
|
||
### Notes
|
||
- Credits/metering intentionally deferred — access is subscription-gated,
|
||
tier gates models. Multi-tenant teams, server-side memory, and the
|
||
browser-direct SSE transport remain as later sub-projects.
|
||
|
||
## [0.4.1] — 2026-05-25
|
||
|
||
### Fixed
|
||
- **Reasoning rendered one token per line in the chat.** The agentic loop
|
||
was emitting a `thinking` event per reasoning token; the plugin relays
|
||
each as a `thought` event, and the browser appends one list item per
|
||
thought — so each reasoning token landed on its own line. Now the loop
|
||
accumulates `reasoning_content` per step and emits a single `thinking`
|
||
event when the step's LLM call finishes (matches the local
|
||
orchestrator's one-thought-per-step model). Content tokens still stream
|
||
individually into the live bubble.
|
||
|
||
## [0.4.0] — 2026-05-25
|
||
|
||
### Added
|
||
- **SSE token streaming.** `GET /v1/runs/:run_id/stream` streams a run's
|
||
live events (`token`, `thinking`, `tool_call`, `tool_result`, `status`,
|
||
`done`, `error`, `end`) with `?since=<seq>` resume. Backed by a per-run
|
||
event buffer in the registry (`addEvent`).
|
||
- `OpenAIClient.chatStream()` — streaming chat completions over fetch:
|
||
parses SSE token deltas, accumulates `content` / `reasoning_content` /
|
||
`tool_calls`, returns the same shape as `chat()`. Connect-only retry
|
||
(safe to retry before any token; never mid-stream).
|
||
- The agentic loop now uses `chatStream` and emits token/tool events into
|
||
the run's buffer as they happen.
|
||
|
||
### Fixed
|
||
- **DeepSeek v4 thinking mode HTTP 400** — the loop now echoes the
|
||
assistant message's `reasoning_content` back on the next turn after a
|
||
tool call, as DeepSeek v4 requires. (`chat()` and `chatStream()` both
|
||
capture the field; the loop re-sends it.)
|
||
|
||
## [0.3.0] — 2026-05-25
|
||
|
||
### Added
|
||
- **Async run model — removes the synchronous timeout ceiling.**
|
||
- `POST /v1/runs/start` registers a run, kicks off the orchestrator in
|
||
the background, and returns `{ run_id, session_id }` immediately.
|
||
- `GET /v1/runs/:run_id/status` reports live progress
|
||
(`status`, `steps_done`, `tools_used`, `elapsed_ms`, `partial_content`)
|
||
and the full PHP-shape `response` once finished.
|
||
- In-memory run registry (`src/orchestrator/registry.ts`) with a 30-min
|
||
TTL for finished runs. `process_request` shares its step/tool arrays
|
||
with the registry so status reflects progress as it happens.
|
||
- Old synchronous `POST /v1/runs` retained for non-browser callers.
|
||
- **Built-in server-side `wait` tool** — lets agents pace long-running
|
||
diagnostics without a DB `SLEEP()` (SQLite has none). Handled in-loop,
|
||
never calls back to the plugin. Max 30s per call.
|
||
- LLM chat retry hardened: 6 attempts, 20s per-attempt timeout (fails
|
||
fast on a hung VPN connection and retries), abort/timeout treated as
|
||
retryable. Step cap raised 25 → 40.
|
||
- `AGENT_STEP tool_call → <name>` logged at info level for live flow
|
||
visibility.
|
||
|
||
## [0.2.1] — 2026-05-25
|
||
|
||
### Added
|
||
- xAI (Grok) provider client — OpenAI-compatible base URL `https://api.x.ai/v1`.
|
||
- DeepSeek provider client — base URL `https://api.deepseek.com/v1`.
|
||
- Provider auto-routing in `pickProvider()`: explicit override → model-name
|
||
prefix (`deepseek*`, `grok*`, `gpt-*`) → first configured key
|
||
(deepseek → xai → openai). `defaultModelFor()` picks a sane model per
|
||
provider.
|
||
- `ALLOW_INSECURE_TLS` (default on in dev) — skips outbound TLS
|
||
verification for machines behind a VPN/MITM whose root CA Node doesn't
|
||
trust. Set off in prod.
|
||
- Tool-manifest sanitizer (`src/tools/manifest.ts`) — coerces PHP's empty
|
||
`[]` to `{}` where objects are required and strips null values, so
|
||
strict validators (DeepSeek) stop rejecting the plugin's 95 tool
|
||
schemas with HTTP 400.
|
||
- LLM chat calls now retry up to 3× with backoff on network errors
|
||
(connect timeout, DNS, TLS) and 5xx/429 — rides through flaky-VPN
|
||
blips. 4xx are treated as real errors and not retried.
|
||
- Detailed logging on tool-exec callback failures (URL, status, headers,
|
||
body length) to diagnose cross-machine / rewrite issues.
|
||
|
||
### Changed
|
||
- Server binds to `0.0.0.0` by default (was `127.0.0.1`) so other LAN
|
||
machines can reach it. Default port `3017`.
|
||
- Default model is now `deepseek-chat`.
|
||
|
||
## [0.2.0] — 2026-05-21
|
||
|
||
### Added
|
||
- `GET /` root route returns an API directory (name, version, endpoint
|
||
hints) — friendlier than the previous 404 when poking around in a
|
||
browser.
|
||
- `POST /v1/runs` — main orchestrator entrypoint. Accepts
|
||
`{ goal, context, options, tools_manifest, callback_url, callback_secret, license_key, site_url }`
|
||
and returns the exact response shape PHP's `wp_ide_process_agentic()`
|
||
uses: `{ success, content, tool_results, execution: { run_id,
|
||
session_id, mode, steps, status_messages, ... }, approval_payload }`.
|
||
- `GET /v1/runs/:run_id` — fetch a stored run record.
|
||
- Greeting + simple paths fully working. Greetings need no LLM call;
|
||
simple invokes OpenAI chat/completions (default `gpt-4o-mini`).
|
||
Agentic path is a placeholder that returns a "step 5" message.
|
||
- Orchestrator router (`src/orchestrator/router.ts`) — port of
|
||
`WP_IDE_AI_Router::classify`; cheap regex/keyword heuristics that pick
|
||
greeting / simple / agentic without an LLM call.
|
||
- OpenAI client over fetch (`src/providers/openai.ts`) — no SDK
|
||
dependency. Provider router (`src/providers/index.ts`) dispatches by
|
||
model name; Anthropic / xAI plug in here in later steps.
|
||
- Runs table CRUD (`src/db/runs.ts`) — persists every run with status
|
||
transitions so a future status endpoint can answer "what happened".
|
||
|
||
### Notes
|
||
- `OPENAI_API_KEY` is read from `.env` on boot. Without it the simple
|
||
path returns a clear actionable error in the standard response shape;
|
||
the greeting path still works because it doesn't call any LLM.
|
||
|
||
## [0.1.0] — 2026-05-21
|
||
|
||
### Added
|
||
- Initial Fastify scaffold (Node 20, TypeScript, ESM).
|
||
- `GET /v1/health` returns name, version, uptime, Node version, timestamp.
|
||
- SQLite via `better-sqlite3` as the dev database; WAL mode + foreign keys
|
||
enabled. Postgres URL recognised for later Coolify deploy but not yet
|
||
wired.
|
||
- Initial schema: `schema_version`, `runs` tables (more added per
|
||
milestone).
|
||
- Zod-validated env loader, pino logger, CORS, graceful shutdown.
|
||
- `start-dev.bat` for one-command Windows dev startup.
|
||
- `Dockerfile` for future Coolify deployment (not used locally).
|
||
- Optional `Caddyfile` for local TLS via `wpide.local`.
|