@terreno/mcp 57.16.1 → 57.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +26 -5
  2. package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +2 -0
  3. package/dist/docs/upgrades/57.17.0.md +126 -0
  4. package/dist/docs/versioned/next/approval-rubric.md +171 -0
  5. package/dist/docs/versioned/next/explanation/admin-interface.md +5 -1
  6. package/dist/docs/versioned/next/explanation/agent-ui-asks.md +1 -0
  7. package/dist/docs/versioned/next/explanation/authentication.md +60 -10
  8. package/dist/docs/versioned/next/explanation/example-coverage.md +2 -1
  9. package/dist/docs/versioned/next/explanation/model-router-actions.md +32 -1
  10. package/dist/docs/versioned/next/how-to/README.md +1 -0
  11. package/dist/docs/versioned/next/how-to/add-github-oauth.md +49 -16
  12. package/dist/docs/versioned/next/how-to/admin-add-model.md +4 -1
  13. package/dist/docs/versioned/next/how-to/ai-feature-development.md +5 -0
  14. package/dist/docs/versioned/next/how-to/background-jobs.md +18 -5
  15. package/dist/docs/versioned/next/how-to/circleci.md +39 -3
  16. package/dist/docs/versioned/next/how-to/configure-better-auth.md +283 -5
  17. package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +3 -1
  18. package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +1 -1
  19. package/dist/docs/versioned/next/how-to/enable-mcp-oauth.md +134 -0
  20. package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +43 -4
  21. package/dist/docs/versioned/next/how-to/install-agent-skills.md +8 -1
  22. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +7 -4
  23. package/dist/docs/versioned/next/how-to/password-reset.md +8 -2
  24. package/dist/docs/versioned/next/how-to/product-announcements.md +5 -3
  25. package/dist/docs/versioned/next/how-to/rate-limiting.md +2 -2
  26. package/dist/docs/versioned/next/how-to/run-tests-locally.md +50 -0
  27. package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  28. package/dist/docs/versioned/next/how-to/websocket-integration.md +42 -9
  29. package/dist/docs/versioned/next/how-to/whats-new-banner.md +55 -0
  30. package/dist/docs/versioned/next/implementationPlans/agent-ui-asks.md +2 -0
  31. package/dist/docs/versioned/next/implementationPlans/app-mcp-server.md +7 -4
  32. package/dist/docs/versioned/next/implementationPlans/fix-1594-cta-realtime-better-auth.md +41 -0
  33. package/dist/docs/versioned/next/implementationPlans/fix-better-auth-sessions-and-config.md +80 -0
  34. package/dist/docs/versioned/next/implementationPlans/rbac-permissions.md +5 -2
  35. package/dist/docs/versioned/next/implementationPlans/rtk-token-source-web-bearer.md +246 -0
  36. package/dist/docs/versioned/next/reference/admin-backend.md +46 -0
  37. package/dist/docs/versioned/next/reference/admin-config.md +2 -2
  38. package/dist/docs/versioned/next/reference/ai.md +48 -4
  39. package/dist/docs/versioned/next/reference/api.md +410 -12
  40. package/dist/docs/versioned/next/reference/blocks.md +1 -1
  41. package/dist/docs/versioned/next/reference/comms.md +19 -1
  42. package/dist/docs/versioned/next/reference/environment-variables.md +6 -1
  43. package/dist/docs/versioned/next/reference/feature-flags.md +1 -1
  44. package/dist/docs/versioned/next/reference/jobs.md +8 -6
  45. package/dist/docs/versioned/next/reference/legacy/rtk.md +145 -18
  46. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +5 -0
  47. package/dist/docs/versioned/next/reference/syncdb.md +64 -3
  48. package/dist/docs/versioned/next/reference/ui.md +23 -1
  49. package/package.json +3 -3
  50. package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +26 -5
  51. package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +2 -0
  52. package/src/docs/upgrades/57.17.0.md +126 -0
  53. package/src/docs/versioned/next/approval-rubric.md +171 -0
  54. package/src/docs/versioned/next/explanation/admin-interface.md +5 -1
  55. package/src/docs/versioned/next/explanation/agent-ui-asks.md +1 -0
  56. package/src/docs/versioned/next/explanation/authentication.md +60 -10
  57. package/src/docs/versioned/next/explanation/example-coverage.md +2 -1
  58. package/src/docs/versioned/next/explanation/model-router-actions.md +32 -1
  59. package/src/docs/versioned/next/how-to/README.md +1 -0
  60. package/src/docs/versioned/next/how-to/add-github-oauth.md +49 -16
  61. package/src/docs/versioned/next/how-to/admin-add-model.md +4 -1
  62. package/src/docs/versioned/next/how-to/ai-feature-development.md +5 -0
  63. package/src/docs/versioned/next/how-to/background-jobs.md +18 -5
  64. package/src/docs/versioned/next/how-to/circleci.md +39 -3
  65. package/src/docs/versioned/next/how-to/configure-better-auth.md +283 -5
  66. package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +3 -1
  67. package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +1 -1
  68. package/src/docs/versioned/next/how-to/enable-mcp-oauth.md +134 -0
  69. package/src/docs/versioned/next/how-to/expose-mcp-tools.md +43 -4
  70. package/src/docs/versioned/next/how-to/install-agent-skills.md +8 -1
  71. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +7 -4
  72. package/src/docs/versioned/next/how-to/password-reset.md +8 -2
  73. package/src/docs/versioned/next/how-to/product-announcements.md +5 -3
  74. package/src/docs/versioned/next/how-to/rate-limiting.md +2 -2
  75. package/src/docs/versioned/next/how-to/run-tests-locally.md +50 -0
  76. package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  77. package/src/docs/versioned/next/how-to/websocket-integration.md +42 -9
  78. package/src/docs/versioned/next/how-to/whats-new-banner.md +55 -0
  79. package/src/docs/versioned/next/implementationPlans/agent-ui-asks.md +2 -0
  80. package/src/docs/versioned/next/implementationPlans/app-mcp-server.md +7 -4
  81. package/src/docs/versioned/next/implementationPlans/fix-1594-cta-realtime-better-auth.md +41 -0
  82. package/src/docs/versioned/next/implementationPlans/fix-better-auth-sessions-and-config.md +80 -0
  83. package/src/docs/versioned/next/implementationPlans/rbac-permissions.md +5 -2
  84. package/src/docs/versioned/next/implementationPlans/rtk-token-source-web-bearer.md +246 -0
  85. package/src/docs/versioned/next/reference/admin-backend.md +46 -0
  86. package/src/docs/versioned/next/reference/admin-config.md +2 -2
  87. package/src/docs/versioned/next/reference/ai.md +48 -4
  88. package/src/docs/versioned/next/reference/api.md +410 -12
  89. package/src/docs/versioned/next/reference/blocks.md +1 -1
  90. package/src/docs/versioned/next/reference/comms.md +19 -1
  91. package/src/docs/versioned/next/reference/environment-variables.md +6 -1
  92. package/src/docs/versioned/next/reference/feature-flags.md +1 -1
  93. package/src/docs/versioned/next/reference/jobs.md +8 -6
  94. package/src/docs/versioned/next/reference/legacy/rtk.md +145 -18
  95. package/src/docs/versioned/next/reference/lifecycle-plugin.md +5 -0
  96. package/src/docs/versioned/next/reference/syncdb.md +64 -3
  97. package/src/docs/versioned/next/reference/ui.md +23 -1
@@ -14,6 +14,7 @@ AI service layer for Terreno backends: provider-agnostic chat via the Vercel AI
14
14
  - [Models](#models)
15
15
  - [Route registrars](#route-registrars)
16
16
  - [AiApp plugin](#aiapp-plugin)
17
+ - [Cost and abuse limits](#cost-and-abuse-limits)
17
18
  - [LangfuseApp plugin](#langfuseapp-plugin)
18
19
  - [Observability](#observability)
19
20
  - [Durable agent harness](#durable-agent-harness)
@@ -284,7 +285,7 @@ AI resolution order: `x-ai-api-key` header + `createModelFn` → `createServerMo
284
285
 
285
286
  Pass `asks: true` (or `{kinds: ["choice"]}`) to let the model ask the user typed questions in the chat. Asks are off by default; with them off, tools, system prompt, and SSE events are unchanged. See [Agent UI Asks](agent-ui-asks.md).
286
287
 
287
- Pass `uiBlocks: true` (or `{hostActions, html, imageHosts, repair, richBlocks, datasetTtlDays, datasetMaxRows}`) to require each assistant reply to be a block document. `html: true` allows `html` blocks and sanitizes them before they are stored. `imageHosts` lists hostnames allowed on `https` image sources. The client receives that document once, after missing action ids are filled, `repair: true` rewrites it, and html is sanitized, and before `{ask}`. `{replace: "text", text}` is sent only when text was already streamed and then changed. `datasetTtlDays` defaults to `0` (keep the dataset). `datasetMaxRows` defaults to 50,000. Off by default; with it off, the system prompt, SSE events, `/gpt/datasets`, and `/gpt/actions` are unchanged. When it is on, the system prompt gains `TERRENO_UI_BLOCKS_SYSTEM_PROMPT` (host callback names included when `hostActions` is set). After the final text, the route validates it and sends `{blocks: {ok, errors, warnings}}` before `{done}`. `hostActions` is the callback allowlist: a name outside it fails with `UNKNOWN_HOST_ACTION`. A stepper or checklist `callback.name` must also name an action whose `handles` is that block; a stepper that names any other registered action fails with `UNKNOWN_HOST_ACTION`. `{repair: true}` runs one repair call when validation fails and stores that reply. A document that is still invalid is stored with a `Block validation errors:` note so the next turn sees it. See [Validate a block document locally](../how-to/agent-ui-blocks.md).
288
+ Pass `uiBlocks: true` (or `{hostActions, html, imageHosts, repair, richBlocks, datasetTtlDays, datasetMaxRows}`) to require each assistant reply to be a block document. `html: true` allows `html` blocks and sanitizes them before they are stored. `imageHosts` lists hostnames allowed on `https` image sources in `image`, `gallery`, and `list` blocks; Markdown images in `text` blocks and plain replies are not checked against it (see [Agent UI asks](../explanation/agent-ui-asks.md)). The client receives that document once, after missing action ids are filled, `repair: true` rewrites it, and html is sanitized, and before `{ask}`. `{replace: "text", text}` is sent only when text was already streamed and then changed. `datasetTtlDays` defaults to `0` (keep the dataset). `datasetMaxRows` defaults to 50,000. Off by default; with it off, the system prompt, SSE events, `/gpt/datasets`, and `/gpt/actions` are unchanged. When it is on, the system prompt gains `TERRENO_UI_BLOCKS_SYSTEM_PROMPT` (host callback names included when `hostActions` is set). After the final text, the route validates it and sends `{blocks: {ok, errors, warnings}}` before `{done}`. `hostActions` is the callback allowlist: a name outside it fails with `UNKNOWN_HOST_ACTION`. A stepper or checklist `callback.name` must also name an action whose `handles` is that block; a stepper that names any other registered action fails with `UNKNOWN_HOST_ACTION`. `{repair: true}` runs one repair call when validation fails and stores that reply. A document that is still invalid is stored with a `Block validation errors:` note so the next turn sees it. See [Validate a block document locally](../how-to/agent-ui-blocks.md).
288
289
 
289
290
  `uiBlocks.richBlocks` defaults to `true`: the prompt offers the rich blocks, and `stepper` when a host action has `handles: "stepper"`. The `checklist` line names the `handles: "checklist"` actions as its callback, or, without one, tells the model to leave `callback` out so ticks stay local. This is a behaviour change on upgrade: every host with `uiBlocks` on gets the new blocks in its prompt. Set `richBlocks: false` while shipped clients (for example older native builds) cannot render them; the prompt is then the same as before rich blocks, even with a stepper action registered. The opt-out changes only what the model is told to write. Validation accepts the rich blocks either way.
290
291
 
@@ -379,7 +380,7 @@ Errors raised before the stream starts return JSON `{status, title, detail, fiel
379
380
 
380
381
  Each `attachments` item is `{type: "image" | "file", url, mimeType, filename?}`. The `url` must be `http(s):` or `data:`. Client-only URLs (`blob:`, `file:`, `content:`, `ph:`) return `400` before streaming starts, because neither the model provider nor a later page load can read them. Upload the file with `POST /files/upload` first, or send it as a `data:` URL.
381
382
 
382
- When `fileStorageService` is set, `data:` attachments are uploaded with `FileStorageService.upload`. The saved user prompt then stores the storage `url` plus `gcsKey`, not the base64 payload. The model still receives the original data for that turn. On later turns, parts with a `gcsKey` are sent to the model as 1-hour signed URLs. A failed upload returns `502 Attachment upload failed`. Without storage, attachments are saved as sent.
383
+ When `fileStorageService` is set, `data:` attachments are uploaded with `FileStorageService.upload`. The saved user prompt then stores the storage `url` plus `gcsKey`, not the base64 payload. The model still receives the original data for that turn. On later turns, a part whose `gcsKey` names a live `FileAttachment` owned by the conversation's owner is sent to the model as a 1-hour signed URL; any other part keeps its stored `url` and is not signed. A custom `fileStorageService` whose `upload` does not create `FileAttachment` records therefore gets no signed URLs on later turns. A failed upload returns `502 Attachment upload failed`. Without storage, attachments are saved as sent.
383
384
 
384
385
  `fileUploadsEnabled` turns uploads off without removing storage. Pass `false`, or a function that returns `false`, and any prompt that includes attachments returns `403 File uploads are disabled` before streaming. Omit it, or pass `true`, to leave uploads enabled. The example app wires this to the `file-uploads` feature flag.
385
386
 
@@ -409,7 +410,7 @@ CRUD at `/gpt/histories` via `modelRouter`:
409
410
  | Create, List | `IsAuthenticated` |
410
411
  | Read, Update, Delete | `IsOwner` |
411
412
 
412
- Query filtered by `userId`; sort `-updated`; query fields `userId`, `projectId`. Create sets `userId` to the caller, so `{}` is a valid create body even when the app validates request bodies. Create and update bodies drop `pendingAsk` (including dotted `pendingAsk.*` paths), so only a chat turn writes it; the OpenAPI spec marks it `readOnly` on create and update.
413
+ Query filtered by `userId`; sort `-updated`; query fields `userId`, `projectId`. Create sets `userId` to the caller, so `{}` is a valid create body even when the app validates request bodies. Create and update bodies drop `pendingAsk` (including dotted `pendingAsk.*` paths), so only a chat turn writes it; the OpenAPI spec marks it `readOnly` on create and update. Create and update bodies also drop any `gcsKey` on `prompts[].content[]` parts (including dotted `prompts.N.content.M.gcsKey` paths) that the history does not already store, so a client can send stored prompts back but cannot add an attachment key.
413
414
 
414
415
  Pass `chat`, the options given to `addGptRoutes`, to add two headless actions for clients that do not read server-sent events, such as a watch app. Both exist only when `chat` turns `asks` on: without `chat`, or with `asks` off, neither action exists, so a host that never turned asks on gets no new endpoints. `AiApp` adds them when its `asks` option is set.
415
416
 
@@ -495,6 +496,45 @@ new AiApp({
495
496
  | `openApiOptions` | Passed to route OpenAPI builders |
496
497
  | `projects` | Default `true`. `false` skips the `/gpt/projects` routes, so `AiApp` never registers the `Project` model |
497
498
 
499
+ ## Cost and abuse limits
500
+
501
+ `@terreno/ai` sets no per-user limit on concurrent turns, tokens, or spend for `/gpt/prompt`
502
+ or the headless turn action (`POST /gpt/histories/:id/turn`). Any signed-in user can start as
503
+ many turns as the host lets through, and each one calls the model with the app's server-side
504
+ key unless the request brings its own `x-ai-api-key`.
505
+
506
+ A reply keeps running after the client disconnects. This is by design: `/gpt/prompt` saves the
507
+ reply as a `streaming` prompt so a reload can resume it through `GET /gpt/histories/:id/stream`,
508
+ and the turn action finishes and saves the turn whether or not the client waits. Closing the tab
509
+ does not stop the model call or its cost.
510
+
511
+ Before exposing AI routes to users, do both:
512
+
513
+ 1. Enable HTTP rate limiting on `TerrenoApp`. It is off unless you pass `rateLimit`. AI routes
514
+ fall in the `api` bucket, which counts requests, not tokens:
515
+
516
+ ```typescript
517
+ new TerrenoApp({
518
+ userModel: User,
519
+ rateLimit: {
520
+ store: "redis", // "memory" (default) | "redis" | "mongo"; use a shared store with more than one instance
521
+ trustProxy: 1, // behind a load balancer such as Cloud Run
522
+ limits: {
523
+ windowMs: 15 * 60 * 1000, // default 15 minutes
524
+ apiMax: 600, // default; lower it to bound chat turns per user or IP
525
+ authMax: 20, // default
526
+ },
527
+ },
528
+ });
529
+ ```
530
+
531
+ For a tighter limit on chat alone, add `createRouteRateLimitMiddleware({name, max, windowMs,
532
+ store, keyBy})` from `@terreno/api` in front of the AI routes. See
533
+ [Rate limiting](../how-to/rate-limiting.md).
534
+ 2. Set a budget, quota, or spend alert with the model provider (for example a Google Cloud
535
+ budget alert and Vertex AI or Gemini API quotas), so a runaway caller or bug is caught by the
536
+ provider even when the app's own limits are loose.
537
+
498
538
  ## LangfuseApp plugin
499
539
 
500
540
  Optional Langfuse admin UI and tracing:
@@ -546,7 +586,7 @@ the `chat-safety-agreement` llm-judge, and the 12-item proofread synthetic datas
546
586
  labeled for 988 vs care-team routing, toxicity, a privacy leak, and a dismissive reply.
547
587
  Admin → Scripts → `seedChatSafetyDataset`, or `bun run script seedChatSafetyDataset --wet` from
548
588
  `example-backend`, loads that set into an already-running database. `SEED_DEFAULTS=true` loads
549
- it on boot, including PR preview. Invalid price JSON or negative/non-numeric prices
589
+ it on boot outside deployed (`NODE_ENV=production`) processes, which ignore the flag. Invalid price JSON or negative/non-numeric prices
550
590
  fail startup with `AI_OBS_PRICE_MAP_JSON` in the error.
551
591
 
552
592
  `POST /ai/example-summarize` (example backend) runs that seeded prompt with
@@ -762,6 +802,8 @@ await mcp.reconnectServer("my-server");
762
802
  await mcp.disconnect();
763
803
  ```
764
804
 
805
+ `getTools()` merges every connected server's tools by name. When two servers offer the same name, the first server in the config keeps it and the later one is dropped with a warning. In a chat turn, MCP tools never replace a route tool or a `createRequestTools` tool of the same name: the MCP tool is dropped with a warning, so a host tool keeps its `execute` and its `needsApproval` policy.
806
+
765
807
  ## Gemini and Vertex helpers
766
808
 
767
809
  **Gemini Developer API** (API-key based):
@@ -788,6 +830,8 @@ const model = vertex.languageModel("gemini-3.8-flash");
788
830
 
789
831
  Env fallbacks: `GOOGLE_VERTEX_PROJECT`, `GOOGLE_VERTEX_LOCATION` (default `global`).
790
832
 
833
+ `languageModel`, `imageModel`, and `isModelAllowed` accept only a plain model id: letters, digits, `.`, `_`, and `-`, with an optional `publishers/google/models/` or `models/` prefix and an optional `@version`. Any other id (one with another `/`, `:`, `?`, or `#`) is refused with `400 Model not permitted`, with or without `allowedModels`. With `allowedModels` set, the id without its prefix and `@version` must match an entry. The provider is given the checked id without its prefix, so a model id sent in a request body cannot name a different model than the one the allow-list checked.
834
+
791
835
  ## Web search types
792
836
 
793
837
  `WebSearchProvider` and `WebSearchResult` define a pluggable search interface for custom Vercel AI SDK tools. The package does not ship a default provider — implement `search(query)` and wire it into a `tool()` passed to `AiApp` `tools`.