Files
devin 178d3b5b0e support for 202507 endpoints (#37)
* [auth][api] accept the 20250424.01 client

* [2025] unstable

* 20250718.0

* correct one this time

* stubs

* more stubs

* more stubs

* [lists] add worker

* [ai] route stubs

* [api] player photo setting

* [econ] add roomEconConfig route

* [infra] update worker generators

* [worker] add cards/moderation/platformnotification workers

* [lists] updates to some endpoints

* [clubs] stub out announcement endpoint, for now

* [econ] stub out season endpoints for now

* [chat] apps/chat stub out party endpoint not sure the shape yet

* [api] stub out statsig and lockeditems

* [doc] new services

* [lists] stub the bulk endpoint

* [datacollection] add placeholder service until we can kill it

* [api] set gifting to lvl5

* update lock

* [cdn] enable cache

* [match] matchmake v2

* [lists] stub some lists

* [ai] stubs

* [rooms] new subroom save endpoint

* [econ] add bulk purchase endpoint

* [discovery] update featured creator to 1 for fun

* [api] add photo settings flag

* [chat] fixup chat permissions (sorta)

* [auth] restrictions endpoint

* [rooms] contributed endpoint

* [api] fix outfit endpoint

* [discovery] attempt to fix store

* [chat] privacy endpoints

* [api] cheered images

* [rooms] add xp endpoint (disbaled)

* [rooms] add xp endpoint (disabled)

* update images-db for cheers

* [rooms] add autocomplete endpoint

* [cdn/img] increase cache ttl for statics

* [api] bulk route for images

* [accounts] add banner image

* [api] add misc missing endpoints

* [discovery] remove AI tab

* [platformnotifications] stub some endpoints

* [lists] add some more lists

* [rooms] additional endpoints

* [chat] stub a few privacy endpoints

* [econ] stub some endpoints

* misc db fixes

* [api] tweak shape for images v6

* [rooms] dont show trending RROs
2026-08-21 15:15:55 -04:00

6.5 KiB

ai

AI worker served on the ai subdomain (ai.recflare.net). The client checks here before offering any of its AI features — Game AI in a room, the Roomie assistant, and Maker AI's usage meter.

No model runs behind this worker, so every answer is static. They are not all refusals, though, because the features fail at different points:

  • Game AI is a server-side feature this server cannot provide, so both of its reads refuse and the client hides it.

  • Roomie runs on the client and only asks this service what it may spend, so the budget reads grant everything. The refusal lands instead on POST /realtime-session/create — the one call whose real answer is a working credential rather than a description of one.

  • Maker AI meters model usage in dollars. Nothing here bills, so every figure is zero.

  • GET / — service status { "service": "ai", "status": "ok" }. No auth.

  • GET /gameai/user/access?roomId=<id>[Authorize]. Whether the caller may use Game AI in a room. Always refused:

    {
      "success": false,
      "error_id": "AI.RoomDoesNotSupportGameAI",
      "error": "This room does not support Rec Room Game AI"
    }
    

    No model runs behind this worker, so every room gets that answer. Two things about it are deliberate: it is a 200, not a 4xx (the client branches on success in the body; an error status would surface as a failed request rather than the "not available here" state this is), and roomId is ignored while the token is still validated first, as the reference server does — so an unauthenticated caller gets a 401 rather than the refusal.

  • GET /gameai/room/<roomId>/spendsummary[Authorize]. What a room has spent of its Game AI budget. Refused for the same reason, but the body is not identical to the access check's:

    {
      "success": false,
      "error_id": "AI.RoomDoesNotSupportGameAI",
      "error": "This room does not support Rec Room Game AI",
      "value": null
    }
    

    It carries value: null where the access check omits the key entirely — that one answers a yes/no and has nothing to carry, while this endpoint's payload slot exists and is simply empty. Reproduced as the reference server sends it; don't unify the two.

  • GET /roomieai/user/access[Authorize]. Roomie AI's energy budget, granted in full:

    {
      "success": true,
      "error_id": null,
      "error": null,
      "value": {
        "MaxEnergyFromSubscriptions": 2147483647,
        "EnergyLeft": 2147483647,
        "NextSubscriptionEnergyRechargeAt": null,
        "OutputAudioEnabled": true
      }
    }
    

    Granted rather than refused because Roomie runs on the CLIENT and only asks this service how much energy it may spend — so for a server that meters nothing, "as much as you can count" is the honest answer. That number is int.MaxValue: the client's field is a signed 32-bit int, and anything larger overflows on the way in and reads as negative, i.e. no energy at all. Nothing depletes, so nothing recharges — hence the null NextSubscriptionEnergyRechargeAt.

    Note the envelope: { success, error_id, error, value }, not the flat body the Game AI check answers with. The two shapes are different on purpose; don't unify them.

  • GET /roomieai/user/facts[Authorize]. What Roomie has been told about the caller:

    { "UserContext": "", "UserFacts": [] }
    

    Live, UserContext is a prose profile written from past conversations and UserFacts holds the discrete (Predicate, Object) claims behind it — things the player told Roomie about themselves. Nothing on this server observes a conversation, so there is nothing to remember and Roomie starts every session knowing nothing about who it's talking to. A flat body, like the Maker AI balances below.

  • GET /makerai/user/balances[Authorize]. What Maker AI has cost the caller, metered live in dollars against a per-user ceiling and a separate RR+ allowance, and rendered by the client as a usage bar with a status word:

    {
      "UsageDollars": 0,
      "UsersMaxUsageDollars": 0,
      "RRPlusUsageDollars": 0,
      "UsersMaxRRPlusUsageDollars": 0,
      "TimeBalanceStatus": "Empty",
      "TimeExpiresAt": "0001-01-01T00:00:00",
      "UsageBalanceStatus": "Good",
      "UsagePercent": 0,
      "RRPlusUsageBalanceStatus": "Good",
      "RRPlusUsagePercent": 0
    }
    

    Zeroed rather than refused: nothing here bills for model usage, so the caller has spent nothing. Both usage buckets report Good — an untouched allowance, not an exhausted one. The time bucket is Empty at DateTime.MinValue, this server selling no timed access for it to hold. Flat body, no envelope.

  • POST /realtime-session/create[Authorize]. Posted when the player actually pulls out an assistant. Live, this mints a short-lived credential ({ SessionId, ClientSecret } in value) that the client then uses to talk to the model provider directly. Refused:

    {
      "success": false,
      "error": "Realtime AI sessions are not available on this server",
      "error_id": "",
      "value": null
    }
    

    This is the one endpoint here whose real answer is a working key rather than a description of one, so there is nothing static to serve — which is why the budget reads above grant everything and the stop lands here instead: the client offers the feature, and the session it opens is what fails. Note error_id is an empty string, not a code — the reference server sends no id for this refusal. The posted body (AIType) is ignored; the answer is the same either way, and a missing or malformed body still gets the refusal rather than a 500.

The worker exists so the client gets a definite answer on the host its endpoints document already names (AIai, see apps/ns), instead of a failed request to a host with nothing behind it.

Not served here: GET /api/makerai/checkfreetrialeligibility, which despite the name lives on the api host in the reference server and belongs to apps/api.

API documentation

GET /openapi.json serves a spec generated from describeRoute blocks that sit alongside each handler, with the schemas in src/openapi.ts. It's also aggregated into the docs page www serves at /docs. A test asserts every route appears in it.

The spec is descriptive, not enforced — same rationale as the other workers: a reverse-engineered protocol, lenient handlers, no runtime validation.

Development

Run in dev mode

pnpm dev

Run tests

pnpm test

Deploy

pnpm turbo deploy