@terreno/mcp 56.0.0-beta.0 → 56.0.0-beta.2

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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bootstrap.js +7 -7
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/docs/templates/bootstrap/backend/types/models/userTypes.template.md +5 -4
  5. package/dist/docs/templates/tools/model.template.md +4 -2
  6. package/dist/docs/upgrades/0.31.0.md +60 -0
  7. package/dist/docs/upgrades/56.0.0-beta.2.md +59 -0
  8. package/dist/docs/versioned/next/README.md +1 -0
  9. package/dist/docs/versioned/next/explanation/README.md +7 -1
  10. package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +65 -0
  11. package/dist/docs/versioned/next/explanation/deployment-baseline.md +78 -0
  12. package/dist/docs/versioned/next/explanation/positioning.md +164 -0
  13. package/dist/docs/versioned/next/explanation/repository-settings.md +44 -0
  14. package/dist/docs/versioned/next/explanation/roadmap-process.md +330 -0
  15. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +887 -0
  16. package/dist/docs/versioned/next/how-to/README.md +8 -0
  17. package/dist/docs/versioned/next/how-to/build-for-web.md +69 -0
  18. package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +115 -0
  19. package/dist/docs/versioned/next/how-to/deploy-to-gcp.md +20 -228
  20. package/dist/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +183 -0
  21. package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +31 -0
  22. package/dist/docs/versioned/next/implementationPlans/README.md +56 -30
  23. package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +2 -1
  24. package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +1 -0
  25. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +115 -0
  26. package/dist/docs/versioned/next/implementationPlans/billing-stripe.md +158 -0
  27. package/dist/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +1 -0
  28. package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +323 -0
  29. package/dist/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +107 -0
  30. package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +157 -0
  31. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +155 -0
  32. package/dist/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +99 -0
  33. package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  34. package/dist/docs/versioned/next/implementationPlans/deploy-to-gcp.md +1 -0
  35. package/dist/docs/versioned/next/implementationPlans/deploy-to-vercel.md +1 -0
  36. package/dist/docs/versioned/next/implementationPlans/deployment-foundation.md +1 -0
  37. package/dist/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -0
  38. package/dist/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +1 -0
  39. package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -0
  40. package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +188 -0
  41. package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +269 -0
  42. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  43. package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  44. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +105 -0
  45. package/dist/docs/versioned/next/implementationPlans/orgs-and-teams.md +158 -0
  46. package/dist/docs/versioned/next/implementationPlans/oss-governance-baseline.md +1 -0
  47. package/dist/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +147 -0
  48. package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -0
  49. package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +1 -0
  50. package/dist/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -0
  51. package/dist/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +1 -0
  52. package/dist/docs/versioned/next/reference/README.md +5 -0
  53. package/dist/docs/versioned/next/reference/admin-spa.md +130 -0
  54. package/dist/docs/versioned/next/reference/ai.md +519 -0
  55. package/dist/docs/versioned/next/reference/comms.md +132 -0
  56. package/dist/docs/versioned/next/reference/environment-variables.md +229 -382
  57. package/dist/docs/versioned/next/reference/test.md +108 -0
  58. package/dist/docs/versioned/next/roadmap.md +9 -0
  59. package/dist/docs/versioned/next/tasks/README.md +42 -0
  60. package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  61. package/dist/docs/versioned/next/tasks/billing-stripe.md +75 -0
  62. package/dist/docs/versioned/next/tasks/comms-abstraction.md +90 -0
  63. package/dist/docs/versioned/next/tasks/comms-adapter-expo-push.md +29 -0
  64. package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +39 -0
  65. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +39 -0
  66. package/dist/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +19 -0
  67. package/dist/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  68. package/dist/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  69. package/dist/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  70. package/dist/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  71. package/dist/docs/versioned/next/tasks/infra-mcp.md +43 -0
  72. package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  73. package/dist/docs/versioned/next/tasks/native-module-baseline.md +42 -0
  74. package/dist/docs/versioned/next/tasks/orgs-and-teams.md +70 -0
  75. package/dist/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  76. package/dist/docs/versioned/next/tasks/password-reset-and-email-verification.md +60 -0
  77. package/dist/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  78. package/dist/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
  79. package/package.json +4 -3
  80. package/src/docs/templates/bootstrap/backend/types/models/userTypes.template.md +5 -4
  81. package/src/docs/templates/tools/model.template.md +4 -2
  82. package/src/docs/upgrades/0.31.0.md +60 -0
  83. package/src/docs/upgrades/56.0.0-beta.2.md +59 -0
  84. package/src/docs/versioned/next/README.md +1 -0
  85. package/src/docs/versioned/next/explanation/README.md +7 -1
  86. package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +65 -0
  87. package/src/docs/versioned/next/explanation/deployment-baseline.md +78 -0
  88. package/src/docs/versioned/next/explanation/positioning.md +164 -0
  89. package/src/docs/versioned/next/explanation/repository-settings.md +44 -0
  90. package/src/docs/versioned/next/explanation/roadmap-process.md +330 -0
  91. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +887 -0
  92. package/src/docs/versioned/next/how-to/README.md +8 -0
  93. package/src/docs/versioned/next/how-to/build-for-web.md +69 -0
  94. package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +115 -0
  95. package/src/docs/versioned/next/how-to/deploy-to-gcp.md +20 -228
  96. package/src/docs/versioned/next/how-to/deploy-web-to-gcs-cdn.md +183 -0
  97. package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +31 -0
  98. package/src/docs/versioned/next/implementationPlans/README.md +56 -30
  99. package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +2 -1
  100. package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +1 -0
  101. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +115 -0
  102. package/src/docs/versioned/next/implementationPlans/billing-stripe.md +158 -0
  103. package/src/docs/versioned/next/implementationPlans/build-terreno-app-validation.md +1 -0
  104. package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +323 -0
  105. package/src/docs/versioned/next/implementationPlans/comms-adapter-expo-push.md +107 -0
  106. package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +157 -0
  107. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-sms.md +155 -0
  108. package/src/docs/versioned/next/implementationPlans/comms-adapter-twilio-verify.md +99 -0
  109. package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +180 -0
  110. package/src/docs/versioned/next/implementationPlans/deploy-to-gcp.md +1 -0
  111. package/src/docs/versioned/next/implementationPlans/deploy-to-vercel.md +1 -0
  112. package/src/docs/versioned/next/implementationPlans/deployment-foundation.md +1 -0
  113. package/src/docs/versioned/next/implementationPlans/docs-reference-coverage.md +1 -0
  114. package/src/docs/versioned/next/implementationPlans/docs-tutorials-ai-first.md +1 -0
  115. package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +1 -0
  116. package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +188 -0
  117. package/src/docs/versioned/next/implementationPlans/infra-mcp.md +269 -0
  118. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +126 -0
  119. package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +269 -0
  120. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +105 -0
  121. package/src/docs/versioned/next/implementationPlans/orgs-and-teams.md +158 -0
  122. package/src/docs/versioned/next/implementationPlans/oss-governance-baseline.md +1 -0
  123. package/src/docs/versioned/next/implementationPlans/password-reset-and-email-verification.md +147 -0
  124. package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -0
  125. package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +1 -0
  126. package/src/docs/versioned/next/implementationPlans/upgrade-guides-and-skill.md +1 -0
  127. package/src/docs/versioned/next/implementationPlans/web-ssr-and-admin-spa.md +1 -0
  128. package/src/docs/versioned/next/reference/README.md +5 -0
  129. package/src/docs/versioned/next/reference/admin-spa.md +130 -0
  130. package/src/docs/versioned/next/reference/ai.md +519 -0
  131. package/src/docs/versioned/next/reference/comms.md +132 -0
  132. package/src/docs/versioned/next/reference/environment-variables.md +229 -382
  133. package/src/docs/versioned/next/reference/test.md +108 -0
  134. package/src/docs/versioned/next/roadmap.md +9 -0
  135. package/src/docs/versioned/next/tasks/README.md +42 -0
  136. package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +5 -5
  137. package/src/docs/versioned/next/tasks/billing-stripe.md +75 -0
  138. package/src/docs/versioned/next/tasks/comms-abstraction.md +90 -0
  139. package/src/docs/versioned/next/tasks/comms-adapter-expo-push.md +29 -0
  140. package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +39 -0
  141. package/src/docs/versioned/next/tasks/comms-adapter-twilio-sms.md +39 -0
  142. package/src/docs/versioned/next/tasks/comms-adapter-twilio-verify.md +19 -0
  143. package/src/docs/versioned/next/tasks/comms-admin-dashboard.md +52 -0
  144. package/src/docs/versioned/next/tasks/deploy-to-gcp.md +11 -11
  145. package/src/docs/versioned/next/tasks/deployment-foundation.md +12 -12
  146. package/src/docs/versioned/next/tasks/docs-reference-coverage.md +4 -4
  147. package/src/docs/versioned/next/tasks/infra-mcp.md +43 -0
  148. package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +215 -0
  149. package/src/docs/versioned/next/tasks/native-module-baseline.md +42 -0
  150. package/src/docs/versioned/next/tasks/orgs-and-teams.md +70 -0
  151. package/src/docs/versioned/next/tasks/oss-governance-baseline.md +18 -18
  152. package/src/docs/versioned/next/tasks/password-reset-and-email-verification.md +60 -0
  153. package/src/docs/versioned/next/tasks/positioning-django-rails-universal.md +4 -4
  154. package/src/docs/versioned/next/tasks/public-roadmap-github.md +15 -15
@@ -0,0 +1,519 @@
1
+ # @terreno/ai
2
+
3
+ AI service layer for Terreno backends: provider-agnostic chat via the Vercel AI SDK, request logging, GPT history, projects, file uploads, MCP tools, and optional Langfuse integration.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Install](#install)
8
+ - [Commands](#commands)
9
+ - [Architecture](#architecture)
10
+ - [Key exports](#key-exports)
11
+ - [AIService](#aiservice)
12
+ - [Structured JSON output](#structured-json-output)
13
+ - [TemperaturePresets](#temperaturepresets)
14
+ - [Models](#models)
15
+ - [Route registrars](#route-registrars)
16
+ - [AiApp plugin](#aiapp-plugin)
17
+ - [LangfuseApp plugin](#langfuseapp-plugin)
18
+ - [Langfuse integration](#langfuse-integration)
19
+ - [FileStorageService](#filestorageservice)
20
+ - [MCPService](#mcpservice)
21
+ - [Gemini and Vertex helpers](#gemini-and-vertex-helpers)
22
+ - [Web search types](#web-search-types)
23
+ - [Integration example](#integration-example)
24
+ - [Environment variables](#environment-variables)
25
+ - [Conventions](#conventions)
26
+ - [Testing](#testing)
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ bun add @terreno/ai @terreno/api mongoose
32
+ ```
33
+
34
+ Peer dependencies: `@terreno/api`, `mongoose` (^8.0.0). Consuming apps also install a Vercel AI SDK provider (e.g. `@ai-sdk/google`) for their chosen model.
35
+
36
+ ## Commands
37
+
38
+ From the `@terreno/ai` package directory:
39
+
40
+ ```bash
41
+ bun run compile # Compile TypeScript
42
+ bun run dev # Watch mode (tsc -w)
43
+ bun run test # Run tests (with bunSetup preload)
44
+ bun run lint # Lint code
45
+ bun run lint:fix # Fix lint issues
46
+ ```
47
+
48
+ ## Architecture
49
+
50
+ ```
51
+ src/
52
+ index.ts # Public exports
53
+ aiApp.ts # AiApp TerrenoPlugin
54
+ langfuseApp.ts # LangfuseApp TerrenoPlugin
55
+ langfuseClient.ts # Langfuse SDK client lifecycle
56
+ langfuseCache.ts # Prompt/trace caching
57
+ langfusePrompts.ts # Prompt compile/fetch helpers
58
+ langfuseTracing.ts # OpenTelemetry tracing setup
59
+ langfuseVercelAi.ts # Bridge Langfuse prompts to Vercel AI SDK
60
+ models/
61
+ aiRequest.ts # AI request logging model
62
+ gptHistory.ts # Conversation history model
63
+ fileAttachment.ts # Uploaded file metadata
64
+ project.ts # GPT project + memories
65
+ routes/
66
+ gpt.ts # Streaming chat, remix, tools, ratings
67
+ gptHistories.ts # History CRUD
68
+ aiRequestsExplorer.ts # Admin request explorer
69
+ files.ts # File upload/signed URL/delete
70
+ projects.ts # Project CRUD + memories
71
+ mcp.ts # MCP server status and tools
72
+ service/
73
+ aiService.ts # Provider-agnostic AI service
74
+ fileStorage.ts # GCS upload helper
75
+ mcpService.ts # MCP client connections
76
+ parseAiJson.ts # LLM JSON normalization/parsing
77
+ prompts.ts # System prompt constants
78
+ gemini.ts # Gemini Developer API model listing
79
+ vertex.ts # Vertex AI provider helpers
80
+ webSearchTool.ts # WebSearchProvider interface
81
+ types/ # Shared TypeScript types
82
+ ```
83
+
84
+ ## Key exports
85
+
86
+ - **Plugins:** `AiApp`, `LangfuseApp`
87
+ - **Service:** `AIService`, `TemperaturePresets`, `FileStorageService`, `MCPService`
88
+ - **Models:** `AIRequest`, `GptHistory`, `FileAttachment`, `Project`
89
+ - **Routes:** `addGptRoutes`, `addGptHistoryRoutes`, `addAiRequestsExplorerRoutes`, `addFileRoutes`, `addProjectRoutes`, `addMcpRoutes`
90
+ - **Structured output:** `parseAiJson`, `normalizeLlmJsonTextForStructuredOutput`, re-exported `Output`, `jsonSchema`, `JSONValue`, `FlexibleSchema` from `ai`
91
+ - **Langfuse:** `initLangfuseClient`, `getLangfuseClient`, `shutdownLangfuseClient`, `compilePrompt`, `createPrompt`, `getPrompt`, `createTelemetryConfig`, `preparePromptForAI`, `initTracing`, `shutdownTracing`, `LangfuseCache`, cache helpers
92
+ - **Gemini / Vertex:** `listGeminiApiModels`, `normalizeGeminiModelId`, `GEMINI_API_BASE_URL`, `createVertexProvider`, `listEnabledVertexModels`, `verifyVertexModelsEnabled`, `assertVertexModelsEnabled`, `isVertexModelAllowed`, `normalizeVertexModelId`, `DEFAULT_VERTEX_LOCATION`
93
+ - **Prompts:** `CONTENT_SUMMARY_PROMPT`, `DEFAULT_GPT_MEMORY`, `JSON_VALUE_SYSTEM_PROMPT`, `REMIX_PROMPT`, `TITLE_GENERATION_PROMPT`, `TRANSLATION_PROMPT`
94
+ - **Web search:** `WebSearchProvider`, `WebSearchResult` types
95
+
96
+ ## AIService
97
+
98
+ Provider-agnostic wrapper around a Vercel AI SDK `LanguageModel`. The consuming app supplies the model instance.
99
+
100
+ ```typescript
101
+ import {AIService} from "@terreno/ai";
102
+ import {google} from "@ai-sdk/google";
103
+
104
+ const aiService = new AIService({
105
+ model: google("gemini-2.5-flash"),
106
+ defaultTemperature: 1.0,
107
+ });
108
+ ```
109
+
110
+ ### Constructor options
111
+
112
+ | Option | Description |
113
+ |--------|-------------|
114
+ | `model` | Vercel AI SDK `LanguageModel` instance (required) |
115
+ | `defaultTemperature` | Default temperature for text/stream calls (default: `TemperaturePresets.DEFAULT`) |
116
+
117
+ ### Properties
118
+
119
+ | Property | Description |
120
+ |----------|-------------|
121
+ | `model` | Configured `LanguageModel` |
122
+ | `defaultTemperature` | Default temperature |
123
+ | `modelId` | Resolved model identifier string |
124
+
125
+ ### Methods
126
+
127
+ | Method | Description |
128
+ |--------|-------------|
129
+ | `generateText(options)` | Non-streaming text generation; logs as `requestType: "general"` |
130
+ | `generateJsonValue(options)` | Any JSON value via `Output.json()`; logs as `"json_value"` |
131
+ | `generateJsonObject(options)` | Typed object from schema/Zod via `Output.object()`; logs as `"json_object"` |
132
+ | `generateJsonArray(options)` | Typed array via `Output.array()`; logs as `"json_array"` |
133
+ | `generateTextStream(options)` | Async generator of text chunks; logs full response after stream completes |
134
+ | `generateRemix(options)` | Reword text using `REMIX_PROMPT` at `TemperaturePresets.BALANCED` |
135
+ | `generateSummary(options)` | Summarize text using `CONTENT_SUMMARY_PROMPT` at `TemperaturePresets.LOW` |
136
+ | `translateText(options)` | Translate text using `TRANSLATION_PROMPT` at `TemperaturePresets.LOW` |
137
+ | `buildMessages(prompts)` | Convert `GptHistoryPrompt[]` to Vercel AI SDK `ModelMessage[]` (skips tool-call/result entries) |
138
+ | `generateChatStream(options)` | Stream multi-turn chat with optional tools; logs prompt as joined message text |
139
+
140
+ All generation methods log to `AIRequest` via private `logRequest()`. Logging failures never throw.
141
+
142
+ ## Structured JSON output
143
+
144
+ `generateJsonValue`, `generateJsonObject`, and `generateJsonArray`:
145
+
146
+ - Default to `TemperaturePresets.DETERMINISTIC` when `temperature` is omitted.
147
+ - Use `JSON_VALUE_SYSTEM_PROMPT` when `systemPrompt` is omitted.
148
+ - Run model text through `normalizeLlmJsonTextForStructuredOutput` before Vercel `Output.*` parsing (strips fences, preamble, balanced JSON slice, trailing commas, smart-quote repair).
149
+ - On failure: `logger.error` records prompt, system prompt, raw model text, and error details; `AIRequest` stores `response` (raw text or sentinel), `error`, and `metadata` (`system`, `finishReason`, `errorStack`, `rawModelTextCaptured`).
150
+
151
+ Standalone helpers:
152
+
153
+ ```typescript
154
+ import {parseAiJson, normalizeLlmJsonTextForStructuredOutput} from "@terreno/ai";
155
+
156
+ const result = parseAiJson<MyType>(rawLlmText);
157
+ if (result.success) {
158
+ console.info(result.data);
159
+ }
160
+ ```
161
+
162
+ Re-exported from `ai` for schema building: `Output`, `jsonSchema`, types `JSONValue`, `FlexibleSchema`.
163
+
164
+ ## TemperaturePresets
165
+
166
+ ```typescript
167
+ import {TemperaturePresets} from "@terreno/ai";
168
+
169
+ TemperaturePresets.DETERMINISTIC // 0
170
+ TemperaturePresets.LOW // 0.3
171
+ TemperaturePresets.BALANCED // 0.7
172
+ TemperaturePresets.DEFAULT // 1.0
173
+ TemperaturePresets.HIGH // 1.5
174
+ TemperaturePresets.MAXIMUM // 2.0
175
+ ```
176
+
177
+ ## Models
178
+
179
+ ### AIRequest
180
+
181
+ Logs all AI calls for monitoring and admin explorer.
182
+
183
+ | Field | Type | Description |
184
+ |-------|------|-------------|
185
+ | `aiModel` | string | Model identifier (field name avoids Mongoose `model` conflict) |
186
+ | `prompt` | string | Input prompt |
187
+ | `requestType` | string | e.g. `general`, `remix`, `summarization`, `translation`, `json_value`, `json_object`, `json_array` |
188
+ | `response` | string? | Response text |
189
+ | `responseTime` | number? | Milliseconds |
190
+ | `tokensUsed` | number? | Total tokens |
191
+ | `userId` | ObjectId? | Requesting user |
192
+ | `error` | string? | Error message |
193
+ | `metadata` | Mixed? | Extra data (e.g. structured-output debug) |
194
+ | `parentRequestId` | ObjectId? | Parent in multi-agent workflow |
195
+ | `subRequestIds` | ObjectId[]? | Child request refs |
196
+ | `totalResponseTime` | number? | Combined sub-request time |
197
+ | `totalTokensUsed` | number? | Combined sub-request tokens |
198
+
199
+ **Statics:** `AIRequest.logRequest(params)`, `AIRequest.logMultiAgentRequest(params)`
200
+
201
+ **Plugins:** `createdUpdatedPlugin`, `isDeletedPlugin`, `findOneOrNone`, `findExactlyOne`
202
+
203
+ ### GptHistory
204
+
205
+ Conversation history with multi-modal prompts.
206
+
207
+ | Field | Type | Description |
208
+ |-------|------|-------------|
209
+ | `userId` | ObjectId | Owner (required) |
210
+ | `title` | string? | Auto-generated on first `/gpt/prompt` response when empty |
211
+ | `projectId` | ObjectId? | Optional project association |
212
+ | `prompts` | array | Messages: `text`, `type` (`user` \| `assistant` \| `system` \| `tool-call` \| `tool-result`), optional `content` parts, `model`, `rating`, tool fields |
213
+
214
+ **Virtual:** `ownerId` aliases `userId` for `Permissions.IsOwner`.
215
+
216
+ ### FileAttachment
217
+
218
+ Metadata for files stored in GCS.
219
+
220
+ | Field | Type | Description |
221
+ |-------|------|-------------|
222
+ | `userId` | ObjectId | Uploader |
223
+ | `filename` | string | Original filename |
224
+ | `gcsKey` | string | Unique GCS object key |
225
+ | `mimeType` | string | MIME type |
226
+ | `size` | number | Bytes |
227
+ | `url` | string | Public GCS URL |
228
+
229
+ **Virtual:** `ownerId` aliases `userId`.
230
+
231
+ ### Project
232
+
233
+ GPT project with persistent context and memories.
234
+
235
+ | Field | Type | Description |
236
+ |-------|------|-------------|
237
+ | `userId` | ObjectId | Owner |
238
+ | `name` | string | Project name |
239
+ | `systemContext` | string | Prepended to every chat in this project |
240
+ | `memories` | array | `{text, category?, source: "user" \| "auto"}` entries |
241
+
242
+ **Virtual:** `ownerId` aliases `userId`.
243
+
244
+ ## Route registrars
245
+
246
+ ### addGptRoutes(router, options)
247
+
248
+ | Endpoint | Method | Auth | Description |
249
+ |----------|--------|------|-------------|
250
+ | `/gpt/prompt` | POST | `IsAuthenticated` | SSE streaming chat; body: `prompt`, optional `historyId`, `systemPrompt`, `attachments`, `model`, `projectId` |
251
+ | `/gpt/remix` | POST | `IsAuthenticated` | Non-streaming text remix; body: `{text}` |
252
+ | `/gpt/histories/:id/rating` | PATCH | `IsAuthenticated` | Rate a prompt; body: `{promptIndex, rating: "up" \| "down" \| null}` |
253
+ | `/gpt/tools` | GET | `IsAuthenticated` | List builtin + MCP tools |
254
+
255
+ AI resolution order: `x-ai-api-key` header + `createModelFn` → `createServerModelFn(modelId)` → configured `aiService` → demo SSE response when `demoMode` and none available.
256
+
257
+ ### addGptHistoryRoutes(router, options?)
258
+
259
+ CRUD at `/gpt/histories` via `modelRouter`:
260
+
261
+ | Operation | Permission |
262
+ |-----------|------------|
263
+ | Create, List | `IsAuthenticated` |
264
+ | Read, Update, Delete | `IsOwner` |
265
+
266
+ Query filtered by `userId`; sort `-updated`; query fields `userId`, `projectId`.
267
+
268
+ ### addProjectRoutes(router, options?)
269
+
270
+ | Endpoint | Method | Auth | Description |
271
+ |----------|--------|------|-------------|
272
+ | `/gpt/projects/:id/memories` | POST | `IsAuthenticated` (owner) | Add memory; body: `{text, category?}` |
273
+ | `/gpt/projects/:id/memories/:memoryId` | DELETE | `IsAuthenticated` (owner) | Remove memory |
274
+ | `/gpt/projects` | CRUD | Create/List: `IsAuthenticated`; Read/Update/Delete: `IsOwner` | Standard modelRouter |
275
+
276
+ ### addFileRoutes(router, options)
277
+
278
+ Requires `fileStorageService` and `gcsBucket` (registered by `AiApp` when both are set).
279
+
280
+ | Endpoint | Method | Auth | Description |
281
+ |----------|--------|------|-------------|
282
+ | `/files/upload` | POST | `IsAuthenticated` | Multipart upload (`file` field); allowed MIME: images, PDF, plain text, CSV, JSON |
283
+ | `/files/*gcsKey` | GET | None | Returns signed read URL (1 hour) |
284
+ | `/files/*gcsKey` | DELETE | `IsAuthenticated` (owner) | Soft-delete attachment and remove from GCS |
285
+
286
+ ### addMcpRoutes(router, options)
287
+
288
+ Requires `mcpService` (registered by `AiApp` when set).
289
+
290
+ | Endpoint | Method | Auth | Description |
291
+ |----------|--------|------|-------------|
292
+ | `/mcp/servers` | GET | `IsAuthenticated` + admin | Server connection status |
293
+ | `/mcp/tools` | GET | `IsAuthenticated` | Available MCP tools |
294
+ | `/mcp/servers/:name/reconnect` | POST | `IsAuthenticated` + admin | Reconnect one server |
295
+
296
+ ### addAiRequestsExplorerRoutes(router, options?)
297
+
298
+ | Endpoint | Method | Auth | Description |
299
+ |----------|--------|------|-------------|
300
+ | `/aiRequestsExplorer` | GET | `IsAuthenticated` + `user.admin` | Paginated AI request log; filters: `requestType`, `model`, `startDate`, `endDate` |
301
+
302
+ ## AiApp plugin
303
+
304
+ `AiApp` registers all AI routes in one `TerrenoPlugin`:
305
+
306
+ ```typescript
307
+ import {AiApp, AIService, FileStorageService, MCPService} from "@terreno/ai";
308
+ import {google} from "@ai-sdk/google";
309
+
310
+ const aiService = new AIService({model: google("gemini-2.5-flash")});
311
+
312
+ new AiApp({
313
+ aiService,
314
+ fileStorageService: new FileStorageService({bucketName: "my-bucket"}),
315
+ gcsBucket: "my-bucket",
316
+ mcpService: new MCPService([{name: "tools", transport: {type: "sse", url: "..."}}]),
317
+ tools: myToolDefinitions,
318
+ demoMode: false,
319
+ createModelFn: (apiKey, modelId) => google(modelId ?? "gemini-2.5-flash", {apiKey}),
320
+ openApiOptions: options,
321
+ }).register(app);
322
+ ```
323
+
324
+ | Option | Description |
325
+ |--------|-------------|
326
+ | `aiService` | Pre-configured server-wide AI service |
327
+ | `createModelFn` | Build model from per-request `x-ai-api-key` |
328
+ | `createServerModelFn` | Server-side model factory (e.g. Vertex ADC) without per-request key |
329
+ | `demoMode` | Return canned responses when no AI service resolves |
330
+ | `fileStorageService` + `gcsBucket` | Enable file upload routes |
331
+ | `mcpService` | Enable MCP routes and tool discovery in chat |
332
+ | `tools` | Static Vercel AI SDK tool definitions for chat |
333
+ | `toolChoice` | `"auto"` \| `"none"` \| `"required"` (default `"auto"` when tools present) |
334
+ | `maxSteps` | Max tool-calling steps (default 5) |
335
+ | `titleModelId` | Cheaper model for conversation title generation |
336
+ | `openApiOptions` | Passed to route OpenAPI builders |
337
+
338
+ ## LangfuseApp plugin
339
+
340
+ Optional Langfuse admin UI and tracing:
341
+
342
+ ```typescript
343
+ import {LangfuseApp} from "@terreno/ai";
344
+
345
+ new LangfuseApp({
346
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
347
+ secretKey: process.env.LANGFUSE_SECRET_KEY!,
348
+ baseUrl: process.env.LANGFUSE_BASE_URL,
349
+ adminPath: "/admin/langfuse",
350
+ enableTracing: true,
351
+ enableAdminUI: true,
352
+ evaluation: {enabled: true, scoringFunctions: [...]},
353
+ }).register(app);
354
+ ```
355
+
356
+ | Option | Default | Description |
357
+ |--------|---------|-------------|
358
+ | `publicKey`, `secretKey` | — | Langfuse API keys (required) |
359
+ | `baseUrl` | — | Langfuse host |
360
+ | `adminPath` | `"/admin/langfuse"` | Admin route prefix |
361
+ | `organization` | maintainer default | Langfuse organization slug — set your own |
362
+ | `project` | `"terreno"` | Langfuse project slug — set your own |
363
+ | `enableTracing` | `true` | OpenTelemetry via `@langfuse/otel` |
364
+ | `enableAdminUI` | `true` | Prompt, trace, playground, evaluation routes |
365
+ | `evaluation.enabled` | — | Register evaluation scoring routes |
366
+ | `cache` | — | Prompt/trace TTL overrides |
367
+
368
+ Calls `shutdownLangfuseClient()` and `shutdownTracing()` on `SIGTERM`.
369
+
370
+ ## Langfuse integration
371
+
372
+ Low-level exports (also used by `addGptRoutes` when `langfuseSystemPromptName` is set):
373
+
374
+ - **Client:** `initLangfuseClient`, `getLangfuseClient`, `isLangfuseInitialized`, `shutdownLangfuseClient`
375
+ - **Prompts:** `getPrompt`, `createPrompt`, `compilePrompt`, `invalidatePromptCache`, `preparePromptForAI`
376
+ - **Tracing:** `initTracing`, `shutdownTracing`, `createTelemetryConfig`
377
+ - **Cache:** `LangfuseCache`, `getCached`, `setCached`, `invalidateCache`
378
+
379
+ Subpath imports for tree-shaking: `@terreno/ai/langfuseClient`, `@terreno/ai/langfuseApp`.
380
+
381
+ ## FileStorageService
382
+
383
+ Google Cloud Storage helper for uploads referenced by `addFileRoutes`.
384
+
385
+ ```typescript
386
+ const storage = new FileStorageService({
387
+ bucketName: "my-bucket",
388
+ storageOptions: {}, // optional @google-cloud/storage options
389
+ });
390
+
391
+ await storage.upload({buffer, filename, mimeType, userId});
392
+ await storage.getSignedUrl(gcsKey); // 1-hour v4 signed URL
393
+ await storage.delete(gcsKey); // GCS delete + soft-delete FileAttachment
394
+ ```
395
+
396
+ ## MCPService
397
+
398
+ Manages SSE MCP client connections for tool calling.
399
+
400
+ ```typescript
401
+ const mcp = new MCPService([
402
+ {name: "my-server", transport: {type: "sse", url: "https://...", headers: {...}}},
403
+ ]);
404
+ await mcp.connect();
405
+ const tools = await mcp.getTools();
406
+ const status = mcp.getServerStatus();
407
+ await mcp.reconnectServer("my-server");
408
+ await mcp.disconnect();
409
+ ```
410
+
411
+ ## Gemini and Vertex helpers
412
+
413
+ **Gemini Developer API** (API-key based):
414
+
415
+ ```typescript
416
+ import {listGeminiApiModels, normalizeGeminiModelId, GEMINI_API_BASE_URL} from "@terreno/ai";
417
+
418
+ const models = await listGeminiApiModels({apiKey: "..."});
419
+ ```
420
+
421
+ **Vertex AI / Gemini Enterprise:**
422
+
423
+ ```typescript
424
+ import {
425
+ createVertexProvider,
426
+ listEnabledVertexModels,
427
+ assertVertexModelsEnabled,
428
+ DEFAULT_VERTEX_LOCATION,
429
+ } from "@terreno/ai";
430
+
431
+ const vertex = await createVertexProvider({project: "my-gcp-project"});
432
+ const model = vertex.languageModel("gemini-2.5-flash");
433
+ ```
434
+
435
+ Env fallbacks: `GOOGLE_VERTEX_PROJECT`, `GOOGLE_VERTEX_LOCATION` (default `us-central1`).
436
+
437
+ ## Web search types
438
+
439
+ `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`.
440
+
441
+ ## Integration example
442
+
443
+ ```typescript
444
+ import {TerrenoApp} from "@terreno/api";
445
+ import {AiApp, AIService, LangfuseApp} from "@terreno/ai";
446
+ import {google} from "@ai-sdk/google";
447
+
448
+ const aiService = new AIService({model: google("gemini-2.5-flash")});
449
+
450
+ new TerrenoApp({userModel: User})
451
+ .register(new AiApp({aiService, openApiOptions: {}}))
452
+ .register(
453
+ new LangfuseApp({
454
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
455
+ secretKey: process.env.LANGFUSE_SECRET_KEY!,
456
+ })
457
+ )
458
+ .start();
459
+ ```
460
+
461
+ Legacy `setupServer` pattern: call `addGptHistoryRoutes`, `addGptRoutes`, etc. inside `addRoutes`.
462
+
463
+ ## Environment variables
464
+
465
+ | Variable | Used by | Description |
466
+ |----------|---------|-------------|
467
+ | `GOOGLE_VERTEX_PROJECT` | `createVertexProvider` | GCP project for Vertex models |
468
+ | `GOOGLE_VERTEX_LOCATION` | `createVertexProvider` | Vertex region (default `us-central1`) |
469
+ | `LANGFUSE_PUBLIC_KEY` | `LangfuseApp` | Langfuse public key |
470
+ | `LANGFUSE_SECRET_KEY` | `LangfuseApp` | Langfuse secret key |
471
+ | `LANGFUSE_BASE_URL` | Langfuse client | Langfuse host URL |
472
+
473
+ GCS credentials use standard Google Cloud Application Default Credentials for `FileStorageService`.
474
+
475
+ ## Conventions
476
+
477
+ - Use `aiModel` on `AIRequest`, not `model` (Mongoose reserved name).
478
+ - `GptHistory`, `FileAttachment`, and `Project` use `userId` with `ownerId` virtual for `Permissions.IsOwner`.
479
+ - Gpt history list uses `queryFilter: (user) => ({userId: user?.id})`, not `OwnerQueryFilter`.
480
+ - Express user in routes: `(req.user as {_id?: ObjectId})` casting pattern.
481
+ - Throw `APIError` with appropriate status; check conditions early.
482
+ - Uses `Model.findOneOrNone` / `findExactlyOne` — never raw `findOne`.
483
+
484
+ ## Testing
485
+
486
+ - Framework: `bun test` with preload `./src/tests/bunSetup.ts`
487
+ - HTTP: supertest against real routes
488
+ - DB: memory Mongo via `@terreno/test` (`TERRENO_TEST_USE_MEMORY_MONGO` or `TERRENO_TEST_MONGODB_URI`)
489
+ - Mock AI model: implement `doGenerate` and `doStream` on a fake `LanguageModel`
490
+
491
+ ```typescript
492
+ const createMockModel = () => ({
493
+ doGenerate: mock(async () => ({
494
+ finishReason: "stop" as const,
495
+ rawCall: {rawPrompt: "", rawSettings: {}},
496
+ text: "response text",
497
+ usage: {completionTokens: 10, promptTokens: 5},
498
+ })),
499
+ doStream: mock(async () => ({
500
+ rawCall: {rawPrompt: "", rawSettings: {}},
501
+ stream: new ReadableStream({
502
+ start(controller) {
503
+ controller.enqueue({type: "text-delta" as const, textDelta: "chunk "});
504
+ controller.enqueue({
505
+ type: "finish" as const,
506
+ finishReason: "stop" as const,
507
+ usage: {completionTokens: 10, promptTokens: 5},
508
+ });
509
+ controller.close();
510
+ },
511
+ }),
512
+ })),
513
+ modelId: "mock-model",
514
+ provider: "mock-provider",
515
+ specificationVersion: "v1" as const,
516
+ });
517
+ ```
518
+
519
+ Never mock `@terreno/api` or Mongoose models — test against real functionality.
@@ -0,0 +1,132 @@
1
+ # @terreno/comms
2
+
3
+ Provider-agnostic transactional communications for Terreno backends. The core package defines
4
+ mail, SMS, push, and verification contracts without installing SendGrid, Twilio, or Expo SDKs.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ bun add @terreno/comms
10
+ ```
11
+
12
+ Peer dependency: `mongoose ^8.0.0 || ^9.0.0`.
13
+
14
+ ## Register the plugin
15
+
16
+ ```typescript
17
+ import {
18
+ CommsApp,
19
+ ConsoleMailProvider,
20
+ ConsolePushProvider,
21
+ ConsoleSmsProvider,
22
+ ConsoleVerificationProvider,
23
+ } from "@terreno/comms";
24
+ import {TerrenoApp} from "@terreno/api";
25
+
26
+ new TerrenoApp({userModel: User})
27
+ .register(
28
+ new CommsApp({
29
+ defaultFrom: "notifications@example.com",
30
+ mail: new ConsoleMailProvider(),
31
+ push: new ConsolePushProvider(),
32
+ sms: new ConsoleSmsProvider(),
33
+ verification: new ConsoleVerificationProvider(),
34
+ })
35
+ )
36
+ .start();
37
+ ```
38
+
39
+ Console providers log only non-sensitive metadata such as message lengths and recipient counts.
40
+ They never log message content, recipient identifiers, push tokens, or verification codes.
41
+
42
+ ## Send messages
43
+
44
+ Use the registered service from routes, jobs, or other plugins:
45
+
46
+ ```typescript
47
+ import {getCommsService} from "@terreno/comms";
48
+
49
+ await getCommsService().sendMail({
50
+ subject: "Welcome",
51
+ text: "Thanks for joining.",
52
+ to: "person@example.com",
53
+ });
54
+
55
+ await getCommsService().sendSms({
56
+ body: "Your appointment is tomorrow.",
57
+ to: "+15555550100",
58
+ });
59
+ ```
60
+
61
+ `sendPushToUser()` resolves active device tokens and deactivates tokens only when a provider marks
62
+ a failure as permanent. `startVerification()` and `checkVerification()` delegate to the configured
63
+ verification provider. `startVerification()` accepts `channel: "sms"` or `channel: "email"`;
64
+ `checkVerification()` verifies the code against the same phone number or email destination and may
65
+ return an `error` reason when `valid` is false. Start attempts store the verification channel in
66
+ delivery-log metadata while recipient values remain redacted.
67
+
68
+ ## Provider contracts
69
+
70
+ | Interface | Method |
71
+ |---|---|
72
+ | `MailProvider` | `sendMail(message)` |
73
+ | `SmsProvider` | `sendSms(message)` |
74
+ | `PushProvider` | `sendPush(message)` — one `SendResult` per token |
75
+ | `VerificationProvider` | `startVerification(options)`, `checkVerification(options)` |
76
+
77
+ Concrete SendGrid, Twilio, and Expo providers are separate adapter packages/subpath exports. Core
78
+ `@terreno/comms` has no concrete provider SDK dependencies.
79
+
80
+ ## Configuration
81
+
82
+ ```typescript
83
+ interface CommsAppOptions {
84
+ basePath?: string; // default: "/comms"
85
+ defaultFrom?: string;
86
+ logMessages?: boolean; // default: true
87
+ mail?: MailProvider;
88
+ onDeliveryEvent?: (event: DeliveryEvent) => Promise<void>;
89
+ push?: PushProvider;
90
+ redactRecipients?: boolean; // default: true
91
+ sms?: SmsProvider;
92
+ verification?: VerificationProvider;
93
+ }
94
+ ```
95
+
96
+ When a channel is unconfigured:
97
+
98
+ - non-production environments use the matching console provider and emit a warning;
99
+ - production throws a `501` `APIError` titled `Comms channel not configured`.
100
+
101
+ Delivery attempts are stored in `CommsMessage`. Recipient values are stored as `[redacted]` unless
102
+ `redactRecipients` is explicitly `false`.
103
+
104
+ ## Routes
105
+
106
+ The default `basePath` is `/comms`.
107
+
108
+ | Method | Path | Access | Purpose |
109
+ |---|---|---|---|
110
+ | `POST` | `/comms/pushTokens` | Authenticated | Register or refresh a device token |
111
+ | `GET` | `/comms/pushTokens` | Authenticated owner | List the current user's tokens |
112
+ | `GET` | `/comms/pushTokens/:id` | Owner | Read one token |
113
+ | `DELETE` | `/comms/pushTokens/:id` | Owner | Deactivate a token |
114
+ | `GET` | `/comms/messages` | Admin | Filtered, paginated delivery log |
115
+
116
+ An active token cannot be claimed by another user. After its owner deactivates it, another
117
+ authenticated user on the same device may register it.
118
+
119
+ ## Templates
120
+
121
+ `renderTemplate()` replaces top-level `{{variable}}` placeholders in `subject`, `text`, and `html`.
122
+ Missing or inherited properties render as empty strings.
123
+
124
+ ```typescript
125
+ const message = renderTemplate({
126
+ data: {name: "Ada"},
127
+ template: {
128
+ subject: "Welcome, {{name}}",
129
+ text: "Hello {{name}}",
130
+ },
131
+ });
132
+ ```