@yottagraph-app/aether-instructions 1.1.27 → 1.1.29
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.
- package/commands/build_my_app.md +3 -3
- package/commands/update_instructions.md +23 -0
- package/package.json +3 -2
- package/rules/aether.mdc +2 -2
- package/rules/agents-data.mdc +72 -0
- package/rules/agents.mdc +32 -107
- package/rules/architecture.mdc +29 -15
- package/rules/cookbook-data.mdc +405 -0
- package/rules/cookbook.mdc +2 -397
- package/rules/{api.mdc → data.mdc} +4 -4
- package/rules/instructions_warning.mdc +2 -2
- package/rules/server-data.mdc +54 -0
- package/rules/server.mdc +2 -48
- package/rules/something-broke.mdc +1 -1
- package/variants/mcp-only/commands/build_my_app.md +146 -0
- package/variants/mcp-only/rules/agents-data.mdc +45 -0
- package/variants/mcp-only/rules/cookbook-data.mdc +50 -0
- package/variants/mcp-only/rules/data.mdc +55 -0
- package/variants/mcp-only/rules/server-data.mdc +41 -0
package/commands/build_my_app.md
CHANGED
|
@@ -102,15 +102,15 @@ Then read these files to understand what's available:
|
|
|
102
102
|
|
|
103
103
|
1. `DESIGN.md` -- project vision and current status
|
|
104
104
|
2. `broadchurch.yaml` -- project config (name, gateway URL, etc.)
|
|
105
|
-
3. **The `
|
|
105
|
+
3. **The `data` cursor rule** -- this is critical. It describes the Query Server, the platform's primary data source. Build against platform APIs, not external sources.
|
|
106
106
|
4. **`.cursor/skills/`** — Each subdirectory is one skill. List them, open each skill’s entry (usually `SKILL.md`) and follow its structure to learn what is documented (APIs, schemas, helpers, etc.).
|
|
107
107
|
5. `.cursor/rules/` -- scan rule names to know what other patterns are available
|
|
108
108
|
|
|
109
|
-
**Important: Use the platform's data.** This app runs on the Lovelace platform, which provides a Query Server with entities, news, filings, sentiment, relationships, events, and more. Read the `
|
|
109
|
+
**Important: Use the platform's data.** This app runs on the Lovelace platform, which provides a Query Server with entities, news, filings, sentiment, relationships, events, and more. Read the `data` rule and the skills under `.cursor/skills/` to understand what data is available. Use `getSchema()` to discover entity types and properties at runtime.
|
|
110
110
|
|
|
111
111
|
Key capabilities:
|
|
112
112
|
|
|
113
|
-
- **Query Server / Elemental API** -- the primary data source. Use `useElementalClient()` from `@yottagraph-app/elemental-api/client`. See the `
|
|
113
|
+
- **Query Server / Elemental API** -- the primary data source. Use `useElementalClient()` from `@yottagraph-app/elemental-api/client`. See the `data` rule.
|
|
114
114
|
- **KV storage** -- always available for preferences and lightweight data (see `pref` rule)
|
|
115
115
|
- **Neon Postgres** -- check if `DATABASE_URL` is in `.env` for database access (see `server` rule)
|
|
116
116
|
- **AI agent chat** -- use the `useAgentChat` composable to build a chat UI for deployed agents
|
|
@@ -100,6 +100,29 @@ cp "$TEMP_DIR/package/commands/"* .cursor/commands/ 2>/dev/null || true
|
|
|
100
100
|
cp -r "$TEMP_DIR/package/skills/"* .cursor/skills/ 2>/dev/null || true
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
### Data-mode variant overlay
|
|
104
|
+
|
|
105
|
+
If this project uses **mcp-only** (or another non-default mode), re-apply the same overlay `init-project.js` uses. Read the saved mode:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
MODE=$(tr -d '\n' < .cursor/.aether-data-mode 2>/dev/null || echo "api-mcp")
|
|
109
|
+
PKG="$TEMP_DIR/package"
|
|
110
|
+
if [ "$MODE" != "api-mcp" ] && [ -d "$PKG/variants/$MODE/rules" ]; then
|
|
111
|
+
cp "$PKG/variants/$MODE/rules/"* .cursor/rules/ 2>/dev/null || true
|
|
112
|
+
fi
|
|
113
|
+
if [ "$MODE" != "api-mcp" ] && [ -d "$PKG/variants/$MODE/commands" ]; then
|
|
114
|
+
cp "$PKG/variants/$MODE/commands/"* .cursor/commands/ 2>/dev/null || true
|
|
115
|
+
fi
|
|
116
|
+
if [ "$MODE" != "api-mcp" ] && [ -d "$PKG/variants/$MODE/skills" ]; then
|
|
117
|
+
cp -r "$PKG/variants/$MODE/skills/"* .cursor/skills/ 2>/dev/null || true
|
|
118
|
+
fi
|
|
119
|
+
if [ "$MODE" = "mcp-only" ]; then
|
|
120
|
+
rm -rf .cursor/skills/elemental-api
|
|
121
|
+
fi
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
If `.cursor/.aether-data-mode` is missing, skip the overlay (defaults to **api-mcp**).
|
|
125
|
+
|
|
103
126
|
---
|
|
104
127
|
|
|
105
128
|
## Step 6: Write Manifest and Version Marker
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottagraph-app/aether-instructions",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.29",
|
|
4
4
|
"description": "Cursor rules, commands, and skills for Aether development",
|
|
5
5
|
"files": [
|
|
6
6
|
"rules",
|
|
7
7
|
"commands",
|
|
8
|
-
"skills"
|
|
8
|
+
"skills",
|
|
9
|
+
"variants"
|
|
9
10
|
],
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
package/rules/aether.mdc
CHANGED
|
@@ -8,7 +8,7 @@ alwaysApply: true
|
|
|
8
8
|
|
|
9
9
|
**Structure:** `pages/` (file-based routing), `components/`, `composables/`, `server/api/`, `agents/` (Python ADK), `mcp-servers/` (Python FastMCP).
|
|
10
10
|
|
|
11
|
-
**Data:**
|
|
11
|
+
**Data:** This app runs on the Lovelace platform -- entities, news, filings, sentiment, relationships, events. See the `data` rule for access patterns and gotchas. Skill docs: `skills/data-model/` (entity types, properties, relationships; `SKILL.md` first). Do NOT call external APIs for data the platform provides.
|
|
12
12
|
|
|
13
13
|
**Storage:** KV (Upstash Redis) for preferences and lightweight state via `Pref<T>` from `usePrefsStore()`. Neon Postgres for relational data if connected (check `.env` for `DATABASE_URL`).
|
|
14
14
|
|
|
@@ -18,4 +18,4 @@ alwaysApply: true
|
|
|
18
18
|
|
|
19
19
|
**First action for a new project:** Run `/build_my_app`.
|
|
20
20
|
|
|
21
|
-
**Task-specific rules:** `architecture` (project structure, navigation, server routes, agents, MCP), `
|
|
21
|
+
**Task-specific rules:** `architecture` (project structure, navigation, server routes, agents, MCP), `data` (Elemental API / data access patterns and gotchas), `cookbook` (copy-paste UI patterns), `cookbook-data` (data-fetching recipes), `design` (DESIGN.md workflow, feature docs), `ui` (page templates, layout patterns), `pref` (KV preferences), `branding` (colors, fonts), `server` (Nitro routes, Neon Postgres), `server-data` (server-side Elemental API from routes), `agents` (ADK agents), `agents-data` (agents calling Elemental API), `something-broke` (error recovery, build failures).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ADK agents calling the Elemental API via broadchurch_auth and local testing env vars."
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: agents/**
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Agents: Elemental API (Query Server)
|
|
8
|
+
|
|
9
|
+
## Connecting to the Elemental API
|
|
10
|
+
|
|
11
|
+
Use `broadchurch_auth` for all Elemental API calls. It lives at
|
|
12
|
+
`agents/broadchurch_auth.py` and is automatically bundled into each agent
|
|
13
|
+
directory at deploy time. It handles:
|
|
14
|
+
|
|
15
|
+
- Routing through the Broadchurch Portal gateway proxy in production
|
|
16
|
+
(no direct QS credentials needed — the portal handles Auth0 M2M auth)
|
|
17
|
+
- Authenticating to the proxy with a per-tenant API key from
|
|
18
|
+
`broadchurch.yaml` (`gateway.qs_api_key`)
|
|
19
|
+
- Falling back to `ELEMENTAL_API_URL` + `ELEMENTAL_API_TOKEN` env vars
|
|
20
|
+
for local dev
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
try:
|
|
24
|
+
from broadchurch_auth import elemental_client
|
|
25
|
+
except ImportError:
|
|
26
|
+
from .broadchurch_auth import elemental_client
|
|
27
|
+
|
|
28
|
+
def get_schema() -> dict:
|
|
29
|
+
"""Get the yottagraph schema."""
|
|
30
|
+
resp = elemental_client.get("/elemental/metadata/schema")
|
|
31
|
+
resp.raise_for_status()
|
|
32
|
+
return resp.json()
|
|
33
|
+
|
|
34
|
+
def find_entities(expression: str, limit: int = 10) -> dict:
|
|
35
|
+
"""Search for entities."""
|
|
36
|
+
resp = elemental_client.post("/elemental/find", data={"expression": expression, "limit": str(limit)})
|
|
37
|
+
resp.raise_for_status()
|
|
38
|
+
return resp.json()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The try/except is required because the import path differs between local
|
|
42
|
+
dev (`agents/` on sys.path → absolute import) and Agent Engine runtime
|
|
43
|
+
(code packaged inside an ADK module → relative import).
|
|
44
|
+
|
|
45
|
+
**Do NOT** hardcode URLs or manually handle auth tokens. Do NOT read
|
|
46
|
+
`broadchurch.yaml` directly — `broadchurch_auth` handles all of this.
|
|
47
|
+
|
|
48
|
+
Key endpoints:
|
|
49
|
+
- `GET /elemental/metadata/schema` — entity types and properties
|
|
50
|
+
- `POST /elemental/find` — search for entities by expression
|
|
51
|
+
- `POST /entities/search` — search for entities by name (batch, scored)
|
|
52
|
+
- `POST /elemental/entities/properties` — get entity property values
|
|
53
|
+
|
|
54
|
+
Requirements for agents using the Elemental API (add to `requirements.txt`):
|
|
55
|
+
```
|
|
56
|
+
google-auth>=2.20.0
|
|
57
|
+
pyyaml>=6.0
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Local testing: Elemental API env vars
|
|
61
|
+
|
|
62
|
+
When testing agents locally with `adk web`, for agents that call the Elemental API:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export ELEMENTAL_API_URL=https://stable-query.lovelace.ai
|
|
66
|
+
export ELEMENTAL_API_TOKEN=<your-token>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
In production, all Elemental API calls go through the Portal Gateway at
|
|
70
|
+
`{gateway_url}/api/qs/{org_id}/...`. The agent sends `X-Api-Key` (from
|
|
71
|
+
`broadchurch.yaml`) and the portal injects its own Auth0 M2M token
|
|
72
|
+
upstream.
|
package/rules/agents.mdc
CHANGED
|
@@ -47,56 +47,8 @@ Key rules:
|
|
|
47
47
|
- Use `google-adk` for the agent framework, `httpx` for HTTP calls
|
|
48
48
|
- Pin dependency versions in `requirements.txt` for reproducible deployments
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Use `broadchurch_auth` for all Elemental API calls. It lives at
|
|
53
|
-
`agents/broadchurch_auth.py` and is automatically bundled into each agent
|
|
54
|
-
directory at deploy time. It handles:
|
|
55
|
-
|
|
56
|
-
- Routing through the Broadchurch Portal gateway proxy in production
|
|
57
|
-
(no direct QS credentials needed — the portal handles Auth0 M2M auth)
|
|
58
|
-
- Authenticating to the proxy with a per-tenant API key from
|
|
59
|
-
`broadchurch.yaml` (`gateway.qs_api_key`)
|
|
60
|
-
- Falling back to `ELEMENTAL_API_URL` + `ELEMENTAL_API_TOKEN` env vars
|
|
61
|
-
for local dev
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
try:
|
|
65
|
-
from broadchurch_auth import elemental_client
|
|
66
|
-
except ImportError:
|
|
67
|
-
from .broadchurch_auth import elemental_client
|
|
68
|
-
|
|
69
|
-
def get_schema() -> dict:
|
|
70
|
-
"""Get the yottagraph schema."""
|
|
71
|
-
resp = elemental_client.get("/elemental/metadata/schema")
|
|
72
|
-
resp.raise_for_status()
|
|
73
|
-
return resp.json()
|
|
74
|
-
|
|
75
|
-
def find_entities(expression: str, limit: int = 10) -> dict:
|
|
76
|
-
"""Search for entities."""
|
|
77
|
-
resp = elemental_client.post("/elemental/find", data={"expression": expression, "limit": str(limit)})
|
|
78
|
-
resp.raise_for_status()
|
|
79
|
-
return resp.json()
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The try/except is required because the import path differs between local
|
|
83
|
-
dev (`agents/` on sys.path → absolute import) and Agent Engine runtime
|
|
84
|
-
(code packaged inside an ADK module → relative import).
|
|
85
|
-
|
|
86
|
-
**Do NOT** hardcode URLs or manually handle auth tokens. Do NOT read
|
|
87
|
-
`broadchurch.yaml` directly — `broadchurch_auth` handles all of this.
|
|
88
|
-
|
|
89
|
-
Key endpoints:
|
|
90
|
-
- `GET /elemental/metadata/schema` — entity types and properties
|
|
91
|
-
- `POST /elemental/find` — search for entities by expression
|
|
92
|
-
- `POST /entities/search` — search for entities by name (batch, scored)
|
|
93
|
-
- `POST /elemental/entities/properties` — get entity property values
|
|
94
|
-
|
|
95
|
-
Requirements for agents using the Elemental API (add to `requirements.txt`):
|
|
96
|
-
```
|
|
97
|
-
google-auth>=2.20.0
|
|
98
|
-
pyyaml>=6.0
|
|
99
|
-
```
|
|
50
|
+
For agents that call the **Elemental API** (`broadchurch_auth`, endpoints,
|
|
51
|
+
local `ELEMENTAL_*` env vars), see the `agents-data` rule.
|
|
100
52
|
|
|
101
53
|
## Local Testing
|
|
102
54
|
|
|
@@ -113,10 +65,6 @@ export GOOGLE_CLOUD_PROJECT=broadchurch
|
|
|
113
65
|
export GOOGLE_CLOUD_LOCATION=us-central1
|
|
114
66
|
export GOOGLE_GENAI_USE_VERTEXAI=1
|
|
115
67
|
|
|
116
|
-
# For agents that call the Elemental API:
|
|
117
|
-
export ELEMENTAL_API_URL=https://stable-query.lovelace.ai
|
|
118
|
-
export ELEMENTAL_API_TOKEN=<your-token>
|
|
119
|
-
|
|
120
68
|
adk web # Opens browser UI at http://127.0.0.1:8000/dev-ui/
|
|
121
69
|
```
|
|
122
70
|
|
|
@@ -163,19 +111,20 @@ adk deploy agent_engine \
|
|
|
163
111
|
|
|
164
112
|
## How Agents Reach the App
|
|
165
113
|
|
|
166
|
-
Once deployed, the
|
|
114
|
+
Once deployed, the app talks to Agent Engine directly — the portal is only
|
|
115
|
+
in the auth path:
|
|
167
116
|
|
|
168
117
|
```
|
|
169
|
-
|
|
170
|
-
→
|
|
171
|
-
→
|
|
118
|
+
Browser → Tenant Nitro Server (POST /api/agent/:agentId/stream)
|
|
119
|
+
→ Portal /authorize (gets short-lived tenant SA token, cached 15 min)
|
|
120
|
+
→ Agent Engine :streamQuery (direct, single hop)
|
|
172
121
|
→ Agent runs (may invoke tools, make multiple LLM calls)
|
|
173
|
-
→
|
|
174
|
-
→ App displays it
|
|
122
|
+
→ Nitro re-emits ADK events as clean SSE to the browser
|
|
175
123
|
```
|
|
176
124
|
|
|
177
125
|
The gateway URL and tenant ID come from `broadchurch.yaml` (injected as
|
|
178
|
-
`NUXT_PUBLIC_GATEWAY_URL` and `NUXT_PUBLIC_TENANT_ORG_ID`).
|
|
126
|
+
`NUXT_PUBLIC_GATEWAY_URL` and `NUXT_PUBLIC_TENANT_ORG_ID`). The token is
|
|
127
|
+
minted for the tenant's GCP service account via SA impersonation.
|
|
179
128
|
|
|
180
129
|
### Agent Discovery
|
|
181
130
|
|
|
@@ -207,7 +156,8 @@ Each agent entry has:
|
|
|
207
156
|
| `engine_id` | `string` | Vertex AI Agent Engine resource ID — used as `{agentId}` in query/stream URLs |
|
|
208
157
|
|
|
209
158
|
The `engine_id` is the key value — it becomes the `{agentId}` path
|
|
210
|
-
parameter in
|
|
159
|
+
parameter in `/api/agent/{agentId}/stream` (the local Nitro route) and
|
|
160
|
+
the portal's `/authorize` endpoint.
|
|
211
161
|
|
|
212
162
|
**How agents get populated:** The portal discovers agents from two sources:
|
|
213
163
|
1. **Firestore** — agents registered by the deploy workflow (`deploy-agent.yml`
|
|
@@ -232,39 +182,24 @@ const agents = config.value?.agents ?? [];
|
|
|
232
182
|
const agentId = agents[0]?.engine_id; // use as {agentId} in query URLs
|
|
233
183
|
```
|
|
234
184
|
|
|
235
|
-
###
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
POST {NUXT_PUBLIC_GATEWAY_URL}/api/agents/{tenantId}/{agentId}/query
|
|
239
|
-
Content-Type: application/json
|
|
240
|
-
|
|
241
|
-
{ "message": "Summarize the latest 8-K filing", "session_id": "optional-session-id" }
|
|
242
|
-
```
|
|
185
|
+
### Streaming via the Local Nitro Route
|
|
243
186
|
|
|
244
|
-
|
|
187
|
+
The `useAgentChat` composable calls `POST /api/agent/:agentId/stream` (the
|
|
188
|
+
local Nitro route). This route handles token acquisition, session creation,
|
|
189
|
+
and SSE parsing — the browser just sees clean Server-Sent Events.
|
|
245
190
|
|
|
246
|
-
|
|
191
|
+
For custom server-side agent calls (e.g. background tasks), you can call the
|
|
192
|
+
portal's `/authorize` endpoint directly and then use the token against
|
|
193
|
+
Agent Engine:
|
|
247
194
|
|
|
248
|
-
```
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
195
|
+
```typescript
|
|
196
|
+
const auth = await $fetch(`${gatewayUrl}/api/agents/${orgId}/${agentId}/authorize`, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
body: { user_id: 'background-task', create_session: false },
|
|
199
|
+
});
|
|
200
|
+
// auth.token, auth.engine_url, auth.expires_in
|
|
254
201
|
```
|
|
255
202
|
|
|
256
|
-
| Field | Type | Description |
|
|
257
|
-
|---|---|---|
|
|
258
|
-
| `output` | `string \| any[]` | Usually the agent's final text. Falls back to the raw `events` array if the gateway couldn't extract text. |
|
|
259
|
-
| `session_id` | `string` | Pass back on subsequent messages to continue the conversation. |
|
|
260
|
-
| `events` | `any[]` | Full ADK event stream. Useful for debugging or building UIs that show intermediate agent steps. |
|
|
261
|
-
|
|
262
|
-
**Important:** `output` is NOT always a string. When the agent's response
|
|
263
|
-
involves complex tool chains, the gateway's server-side extraction may miss
|
|
264
|
-
the text and return the raw events array instead. Always use
|
|
265
|
-
`extractAgentText()` to parse `output` safely rather than treating it as a
|
|
266
|
-
string directly.
|
|
267
|
-
|
|
268
203
|
### ADK Event Stream Format
|
|
269
204
|
|
|
270
205
|
The `events` array contains one object per step the agent took. Each event
|
|
@@ -292,13 +227,11 @@ than objects — always handle both.
|
|
|
292
227
|
|
|
293
228
|
### Parsing Agent Responses (Non-Streaming)
|
|
294
229
|
|
|
295
|
-
For
|
|
296
|
-
|
|
230
|
+
For non-streaming use cases (e.g. extracting text from a buffered response),
|
|
231
|
+
use `extractAgentText`:
|
|
297
232
|
|
|
298
233
|
```typescript
|
|
299
234
|
import { extractAgentText } from '~/composables/useAgentChat';
|
|
300
|
-
|
|
301
|
-
const response = await $fetch(url, { method: 'POST', body: { message } });
|
|
302
235
|
const text = extractAgentText(response.output);
|
|
303
236
|
```
|
|
304
237
|
|
|
@@ -307,28 +240,20 @@ JSON-string or object elements), single event objects, and several legacy
|
|
|
307
240
|
Agent Engine response shapes. It skips `functionCall` / `functionResponse`
|
|
308
241
|
events and returns the agent's final text.
|
|
309
242
|
|
|
310
|
-
### Streaming
|
|
243
|
+
### Streaming SSE Events
|
|
311
244
|
|
|
312
|
-
The
|
|
313
|
-
Events
|
|
314
|
-
default** — it tries `/stream` first and falls back to `/query`
|
|
245
|
+
The local Nitro route (`POST /api/agent/:agentId/stream`) returns
|
|
246
|
+
Server-Sent Events. The `useAgentChat` composable handles these
|
|
315
247
|
automatically.
|
|
316
248
|
|
|
317
|
-
|
|
318
|
-
POST {NUXT_PUBLIC_GATEWAY_URL}/api/agents/{tenantId}/{agentId}/stream
|
|
319
|
-
Content-Type: application/json
|
|
320
|
-
|
|
321
|
-
{ "message": "Summarize the latest 8-K filing", "session_id": "optional" }
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
The response is an SSE stream with these event types:
|
|
249
|
+
SSE event types:
|
|
325
250
|
|
|
326
251
|
| Event | Data Shape | Description |
|
|
327
252
|
|---|---|---|
|
|
328
253
|
| `text` | `{ "text": "..." }` | Agent text output (replaces previous text) |
|
|
329
254
|
| `function_call` | `{ "name": "...", "args": {...} }` | Agent is calling a tool |
|
|
330
255
|
| `function_response` | `{ "name": "...", "response": {...} }` | Tool returned a result |
|
|
331
|
-
| `error` | `{ "message": "..." }` | Error during processing |
|
|
256
|
+
| `error` | `{ "message": "...", "code": "..." }` | Error during processing (`code` is `PERMISSION_DENIED` for IAM failures) |
|
|
332
257
|
| `done` | `{ "session_id": "...", "text": "..." }` | Stream complete with final text |
|
|
333
258
|
|
|
334
259
|
For custom agent UIs that need streaming, import `readSSE`:
|
package/rules/architecture.mdc
CHANGED
|
@@ -9,7 +9,7 @@ Aether is an app framework built on Nuxt 3 + Vue 3 + Vuetify 3 + TypeScript. It
|
|
|
9
9
|
|
|
10
10
|
**Tech stack**: Nuxt 3 (SPA), Vue 3 Composition API (`<script setup>`), Vuetify 3, TypeScript (required), Auth0 (automatic).
|
|
11
11
|
|
|
12
|
-
**Data source**: This app runs on the Lovelace platform
|
|
12
|
+
**Data source**: This app runs on the Lovelace platform (entities, news, filings, sentiment, relationships, events). See the `data` rule for how your app accesses that data. Do not call external APIs for data the platform provides.
|
|
13
13
|
|
|
14
14
|
## Project Structure
|
|
15
15
|
|
|
@@ -28,11 +28,10 @@ mcp-servers/ # MCP servers (Python, deploy to Cloud Run)
|
|
|
28
28
|
|
|
29
29
|
| Store | Purpose | When to use |
|
|
30
30
|
|---|---|---|
|
|
31
|
-
| Query Server
|
|
31
|
+
| Platform data (Query Server / Postgres per your architecture) | Lovelace knowledge graph or synced local data | See the `data` rule |
|
|
32
32
|
| KV (Upstash Redis) | User preferences, lightweight state | Settings, watchlists, UI state that should persist |
|
|
33
33
|
| Neon Postgres | App-specific relational data | Custom tables, complex queries (if connected — check `DATABASE_URL` in `.env`) |
|
|
34
34
|
|
|
35
|
-
Use `useElementalClient()` for Query Server data (see `api` rule).
|
|
36
35
|
Use `Pref<T>` or `usePrefsStore()` for KV (see `pref` rule).
|
|
37
36
|
Check `.env` for `DATABASE_URL` before using Neon Postgres (see `server` rule).
|
|
38
37
|
|
|
@@ -113,12 +112,12 @@ using them later when adding features to an established app.
|
|
|
113
112
|
## Server Routes
|
|
114
113
|
|
|
115
114
|
Nitro server routes live in `server/api/` and deploy with the app to Vercel.
|
|
116
|
-
Use server routes when you need to proxy external APIs (avoid CORS),
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
Use server routes when you need to proxy external APIs (avoid CORS), access
|
|
116
|
+
data server-side, or keep secrets off the client. Call them from client code
|
|
117
|
+
with `$fetch('/api/my-data/fetch')`.
|
|
119
118
|
|
|
120
|
-
See the `server` rule for file-routing conventions
|
|
121
|
-
|
|
119
|
+
See the `server` rule for file-routing conventions and Neon Postgres patterns.
|
|
120
|
+
See `server-data` when calling the platform Query Server from server routes.
|
|
122
121
|
|
|
123
122
|
## Beyond the UI: Agents, MCP Servers, and Server Routes
|
|
124
123
|
|
|
@@ -126,7 +125,21 @@ Aether apps are more than just a Nuxt SPA. The project contains three additional
|
|
|
126
125
|
|
|
127
126
|
### `agents/` -- ADK Agents (Python)
|
|
128
127
|
|
|
129
|
-
Each subdirectory is a self-contained Python agent that deploys to Vertex AI Agent Engine. See the `agents` cursor rule for development patterns. Agents are deployed via the Broadchurch Portal UI or the `/deploy_agent` Cursor command, both of which trigger `deploy-agent.yml`.
|
|
128
|
+
Each subdirectory is a self-contained Python agent that deploys to Vertex AI Agent Engine. See the `agents` cursor rule for development patterns. Agents are deployed via the Broadchurch Portal UI or the `/deploy_agent` Cursor command, both of which trigger `deploy-agent.yml`. Use the `useAgentChat` composable to build a chat UI that talks to them.
|
|
129
|
+
|
|
130
|
+
**Agent query path:** The app talks to Agent Engine directly — the portal is
|
|
131
|
+
only in the auth path. The flow is:
|
|
132
|
+
|
|
133
|
+
1. Tenant Nitro route (`server/api/agent/[agentId]/stream.post.ts`) calls the
|
|
134
|
+
Portal Gateway's `/authorize` endpoint to get a short-lived (15 min) GCP
|
|
135
|
+
access token minted for the tenant's service account.
|
|
136
|
+
2. The Nitro route uses that token to call Agent Engine's `:streamQuery`
|
|
137
|
+
directly — streaming data does NOT flow through the portal.
|
|
138
|
+
3. Tokens are cached server-side for their TTL.
|
|
139
|
+
|
|
140
|
+
If you see a 403 from Agent Engine, the tenant's service account is missing
|
|
141
|
+
IAM permissions (`roles/aiplatform.user`). Check the Broadchurch portal's
|
|
142
|
+
project detail page for IAM health status.
|
|
130
143
|
|
|
131
144
|
### `mcp-servers/` -- MCP Servers (Python)
|
|
132
145
|
|
|
@@ -141,14 +154,15 @@ Tenant-specific configuration generated during provisioning. Contains GCP projec
|
|
|
141
154
|
The template ships composables for interacting with the Lovelace platform.
|
|
142
155
|
Use these to build whatever UI fits the app:
|
|
143
156
|
|
|
144
|
-
- **`useAgentChat()`** -- Send messages to deployed ADK agents
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
- **`useAgentChat()`** -- Send messages to deployed ADK agents. Uses the
|
|
158
|
+
local Nitro streaming route (`/api/agent/:agentId/stream`) which calls
|
|
159
|
+
Agent Engine directly with a tenant SA token. Handles streaming, session
|
|
160
|
+
management, and response parsing. Shows clear error messages when IAM
|
|
161
|
+
permissions are missing. See the `agents` cursor rule for details.
|
|
148
162
|
- **`useTenantConfig()`** -- Fetch the tenant's runtime config (deployed
|
|
149
163
|
agents, feature flags, MCP servers) from the Portal Gateway.
|
|
150
|
-
- **`useElementalClient()`** --
|
|
151
|
-
|
|
164
|
+
- **`useElementalClient()`** -- When using the Elemental API from the client,
|
|
165
|
+
see the `data` rule.
|
|
152
166
|
- **`usePrefsStore()` / `Pref<T>`** -- KV-backed user preferences. See
|
|
153
167
|
the `pref` rule.
|
|
154
168
|
|