@yottagraph-app/aether-instructions 1.1.3 → 1.1.5
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/package.json +1 -1
- package/rules/agents.mdc +179 -4
- package/rules/api.mdc +146 -5
- package/rules/architecture.mdc +17 -5
- package/rules/cookbook.mdc +1 -1
package/package.json
CHANGED
package/rules/agents.mdc
CHANGED
|
@@ -168,14 +168,189 @@ Once deployed, the agent is reachable through the Portal Gateway:
|
|
|
168
168
|
```
|
|
169
169
|
Chat UI (pages/chat.vue)
|
|
170
170
|
→ POST NUXT_PUBLIC_GATEWAY_URL/api/agents/{tenantId}/{agentId}/query
|
|
171
|
-
→ Portal Gateway proxies to Vertex AI Agent Engine
|
|
172
|
-
→ Agent runs,
|
|
171
|
+
→ Portal Gateway proxies to Vertex AI Agent Engine (streamQuery)
|
|
172
|
+
→ Agent runs (may invoke tools, make multiple LLM calls)
|
|
173
|
+
→ Gateway collects the ADK event stream, extracts final text
|
|
173
174
|
→ Chat UI displays it
|
|
174
175
|
```
|
|
175
176
|
|
|
176
177
|
The gateway URL and tenant ID come from `broadchurch.yaml` (injected as
|
|
177
|
-
`NUXT_PUBLIC_GATEWAY_URL` and `NUXT_PUBLIC_TENANT_ORG_ID`).
|
|
178
|
-
|
|
178
|
+
`NUXT_PUBLIC_GATEWAY_URL` and `NUXT_PUBLIC_TENANT_ORG_ID`).
|
|
179
|
+
|
|
180
|
+
### Agent Discovery
|
|
181
|
+
|
|
182
|
+
The chat page discovers deployed agents by fetching the tenant config:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
GET {NUXT_PUBLIC_GATEWAY_URL}/api/config/{NUXT_PUBLIC_TENANT_ORG_ID}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The response includes an `agents` array:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"agents": [
|
|
193
|
+
{ "name": "filing_analyst", "display_name": "Filing Analyst", "engine_id": "1234567890" },
|
|
194
|
+
{ "name": "research_bot", "display_name": "Research Bot", "engine_id": "0987654321" }
|
|
195
|
+
],
|
|
196
|
+
"features": { "chat": true, ... },
|
|
197
|
+
...
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Each agent entry has:
|
|
202
|
+
|
|
203
|
+
| Field | Type | Description |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| `name` | `string` | Agent directory name (e.g. `filing_analyst`) |
|
|
206
|
+
| `display_name` | `string` | Human-readable name for the UI |
|
|
207
|
+
| `engine_id` | `string` | Vertex AI Agent Engine resource ID — used as `{agentId}` in query/stream URLs |
|
|
208
|
+
|
|
209
|
+
The `engine_id` is the key value — it becomes the `{agentId}` path
|
|
210
|
+
parameter in `POST /api/agents/{tenantId}/{agentId}/query`.
|
|
211
|
+
|
|
212
|
+
**How agents get populated:** The portal discovers agents from two sources:
|
|
213
|
+
1. **Firestore** — agents registered by the deploy workflow (`deploy-agent.yml`
|
|
214
|
+
calls `POST /api/agents/{tenantId}` to register)
|
|
215
|
+
2. **Agent Engine API** — the portal also queries Vertex AI for reasoning
|
|
216
|
+
engines whose display name starts with the project name (e.g.
|
|
217
|
+
`my-project--filing_analyst`), catching agents that were deployed but
|
|
218
|
+
not yet registered
|
|
219
|
+
|
|
220
|
+
Both sources are merged and deduplicated by name. If the config endpoint
|
|
221
|
+
returns an empty `agents` array, no agents have been deployed yet.
|
|
222
|
+
|
|
223
|
+
Use `useTenantConfig()` to fetch this config from Vue code:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { useTenantConfig } from '~/composables/useTenantConfig';
|
|
227
|
+
|
|
228
|
+
const { config, fetchConfig } = useTenantConfig();
|
|
229
|
+
await fetchConfig();
|
|
230
|
+
|
|
231
|
+
const agents = config.value?.agents ?? [];
|
|
232
|
+
const agentId = agents[0]?.engine_id; // use as {agentId} in query URLs
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Gateway Request
|
|
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
|
+
```
|
|
243
|
+
|
|
244
|
+
Omit `session_id` on the first message — the gateway auto-creates one.
|
|
245
|
+
|
|
246
|
+
### Gateway Response Format
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"output": "The agent's final text response",
|
|
251
|
+
"session_id": "session-abc-123",
|
|
252
|
+
"events": [ /* raw ADK event stream */ ]
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
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
|
+
### ADK Event Stream Format
|
|
269
|
+
|
|
270
|
+
The `events` array contains one object per step the agent took. Each event
|
|
271
|
+
has `content.parts[]` where each part is one of:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{ "text": "The agent's text response..." }
|
|
275
|
+
{ "functionCall": { "name": "search", "args": { "q": "AAPL" } } }
|
|
276
|
+
{ "functionResponse": { "name": "search", "response": { "results": [...] } } }
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
A typical stream for an agent that uses a tool:
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
[
|
|
283
|
+
{ "content": { "parts": [{ "functionCall": { "name": "search", "args": {"q": "AAPL 8-K"} } }], "role": "model" } },
|
|
284
|
+
{ "content": { "parts": [{ "functionResponse": { "name": "search", "response": {"results": ["..."]} } }], "role": "tool" } },
|
|
285
|
+
{ "content": { "parts": [{ "text": "Here is the summary of the 8-K filing..." }], "role": "model" } }
|
|
286
|
+
]
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The final text is the last `text` part that isn't in a `functionCall` or
|
|
290
|
+
`functionResponse` event. Events may also arrive as JSON strings rather
|
|
291
|
+
than objects — always handle both.
|
|
292
|
+
|
|
293
|
+
### Parsing Agent Responses (Non-Streaming)
|
|
294
|
+
|
|
295
|
+
For one-shot calls (server routes, background tasks) where streaming isn't
|
|
296
|
+
needed, use the buffered `/query` endpoint with `extractAgentText`:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { extractAgentText } from '~/composables/useAgentChat';
|
|
300
|
+
|
|
301
|
+
const response = await $fetch(url, { method: 'POST', body: { message } });
|
|
302
|
+
const text = extractAgentText(response.output);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`extractAgentText` handles: plain strings, ADK event stream arrays (with
|
|
306
|
+
JSON-string or object elements), single event objects, and several legacy
|
|
307
|
+
Agent Engine response shapes. It skips `functionCall` / `functionResponse`
|
|
308
|
+
events and returns the agent's final text.
|
|
309
|
+
|
|
310
|
+
### Streaming Responses
|
|
311
|
+
|
|
312
|
+
The gateway also exposes a streaming endpoint that returns Server-Sent
|
|
313
|
+
Events as the agent executes. **The `useAgentChat` composable uses this by
|
|
314
|
+
default** — it tries `/stream` first and falls back to `/query`
|
|
315
|
+
automatically.
|
|
316
|
+
|
|
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:
|
|
325
|
+
|
|
326
|
+
| Event | Data Shape | Description |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| `text` | `{ "text": "..." }` | Agent text output (replaces previous text) |
|
|
329
|
+
| `function_call` | `{ "name": "...", "args": {...} }` | Agent is calling a tool |
|
|
330
|
+
| `function_response` | `{ "name": "...", "response": {...} }` | Tool returned a result |
|
|
331
|
+
| `error` | `{ "message": "..." }` | Error during processing |
|
|
332
|
+
| `done` | `{ "session_id": "...", "text": "..." }` | Stream complete with final text |
|
|
333
|
+
|
|
334
|
+
For custom agent UIs that need streaming, import `readSSE`:
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { readSSE } from '~/composables/useAgentChat';
|
|
338
|
+
|
|
339
|
+
const res = await fetch(streamUrl, {
|
|
340
|
+
method: 'POST',
|
|
341
|
+
headers: { 'Content-Type': 'application/json' },
|
|
342
|
+
body: JSON.stringify({ message: 'Hello' }),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
for await (const { event, data } of readSSE(res)) {
|
|
346
|
+
if (event === 'text') console.log('Agent says:', data.text);
|
|
347
|
+
if (event === 'function_call') console.log('Calling:', data.name);
|
|
348
|
+
if (event === 'done') console.log('Session:', data.session_id);
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
The `done` event always includes the final extracted text, so you don't
|
|
353
|
+
need to track text deltas yourself.
|
|
179
354
|
|
|
180
355
|
## Agent Design Guidelines
|
|
181
356
|
|
package/rules/api.mdc
CHANGED
|
@@ -20,7 +20,9 @@ For full endpoint documentation, read the **elemental-api skill** in
|
|
|
20
20
|
`skills/elemental-api/`. Start with `SKILL.md`, then `overview.md`.
|
|
21
21
|
These files are copied from `@yottagraph-app/elemental-api-skill` during
|
|
22
22
|
`npm install` (postinstall step) — if the directory is empty, run
|
|
23
|
-
`npm install` first.
|
|
23
|
+
`npm install` first. The skill docs contain detailed response shapes and
|
|
24
|
+
edge cases that go beyond this rule's quick reference — **run `npm install`
|
|
25
|
+
early** to make them available during initial exploration.
|
|
24
26
|
|
|
25
27
|
Key files:
|
|
26
28
|
- `entities.md` — entity search, details, and properties
|
|
@@ -51,6 +53,57 @@ Types are also imported from the client:
|
|
|
51
53
|
import type { NamedEntityReport, GetNEIDResponse } from '@yottagraph-app/elemental-api/client';
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
### Client Method Quick Reference
|
|
57
|
+
|
|
58
|
+
All methods return data directly and throw on non-2xx responses.
|
|
59
|
+
|
|
60
|
+
**Entity search and lookup:**
|
|
61
|
+
|
|
62
|
+
| Method | Signature | Purpose |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `getNEID` | `(params: { entityName, maxResults?, includeNames? })` | Lookup entity by name |
|
|
65
|
+
| `findEntities` | `(body: FindEntitiesBody)` | Expression-based search (see `find.md`) |
|
|
66
|
+
| `getNamedEntityReport` | `(neid: string)` | Entity details (name, aliases, type) |
|
|
67
|
+
| `getEntityDetails` | `(neid: string)` | Alias for entity reports |
|
|
68
|
+
|
|
69
|
+
**Properties and schema:**
|
|
70
|
+
|
|
71
|
+
| Method | Signature | Purpose |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `getSchema` | `()` | All entity types (flavors) and properties (PIDs) |
|
|
74
|
+
| `getPropertyValues` | `(body: { eids: string, pids: string })` | Property values (eids/pids are JSON-stringified arrays!) |
|
|
75
|
+
| `summarizeProperty` | `(pid: number)` | Summary stats for a property |
|
|
76
|
+
|
|
77
|
+
**Relationships and graph:**
|
|
78
|
+
|
|
79
|
+
| Method | Signature | Purpose |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `getLinkedEntities` | `(neid, params?: { entity_type?, link_type? })` | Linked entities (person/org/location only) |
|
|
82
|
+
| `getLinks` | `(sourceNeid, targetNeid, params?)` | Links between two specific entities |
|
|
83
|
+
| `getLinkCounts` | `(sourceNeid, targetNeid)` | Link counts between entities |
|
|
84
|
+
| `getNeighborhood` | `(centerNeid, params?)` | Neighboring entities |
|
|
85
|
+
| `getGraphLayout` | `(centerNeid, params?)` | Graph layout for visualization |
|
|
86
|
+
|
|
87
|
+
**News, events, and sentiment:**
|
|
88
|
+
|
|
89
|
+
| Method | Signature | Purpose |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `getArticle` | `(artid: string)` | Article by ID |
|
|
92
|
+
| `getArticleText` | `(artid: string)` | Article full text |
|
|
93
|
+
| `getEvent` | `(eveid: string)` | Event by ID |
|
|
94
|
+
| `getEventsForEntity` | `(params: { neid, startTime?, endTime? })` | Events involving an entity |
|
|
95
|
+
| `getMentions` | `(params: { neid, startTime?, endTime? })` | Mention codes for entities |
|
|
96
|
+
| `getMentionCounts` | `(params: { neid, ... })` | Bucketed mention counts |
|
|
97
|
+
| `getNamedEntitySentiment` | `(neid: string)` | Sentiment for an entity |
|
|
98
|
+
|
|
99
|
+
**Other:**
|
|
100
|
+
|
|
101
|
+
| Method | Signature | Purpose |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `getHealth` | `()` | Health check |
|
|
104
|
+
| `getStatus` | `()` | Server status and capabilities |
|
|
105
|
+
| `adaMessage` | `(body: AdaMessageBody)` | Ada AI chat |
|
|
106
|
+
|
|
54
107
|
## Discovery-First Pattern
|
|
55
108
|
|
|
56
109
|
The knowledge graph contains many entity types and properties, and new datasets
|
|
@@ -81,17 +134,49 @@ knowledge of what's in the graph.
|
|
|
81
134
|
|
|
82
135
|
## API Gotchas
|
|
83
136
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
### `getSchema()` response is nested — WILL crash if you don't handle it
|
|
138
|
+
|
|
139
|
+
The generated TypeScript types suggest `response.properties` and
|
|
140
|
+
`response.flavors` exist at the top level. **They don't.** The API nests
|
|
141
|
+
them under `response.schema`. This mismatch between types and reality
|
|
142
|
+
causes `Cannot read properties of undefined` every time.
|
|
88
143
|
|
|
89
144
|
```typescript
|
|
145
|
+
// WRONG — will crash at runtime despite TypeScript compiling fine:
|
|
146
|
+
const res = await client.getSchema();
|
|
147
|
+
const props = res.properties; // undefined!
|
|
148
|
+
|
|
149
|
+
// CORRECT — always access through .schema:
|
|
90
150
|
const res = await client.getSchema();
|
|
91
151
|
const properties = res.schema?.properties ?? (res as any).properties ?? [];
|
|
92
152
|
const flavors = res.schema?.flavors ?? (res as any).flavors ?? [];
|
|
93
153
|
```
|
|
94
154
|
|
|
155
|
+
The `(res as any).properties` fallback is there in case the API is ever
|
|
156
|
+
fixed to match the types. Use this pattern every time.
|
|
157
|
+
|
|
158
|
+
### `getNamedEntityReport()` response is nested under `.report`
|
|
159
|
+
|
|
160
|
+
Same problem as `getSchema()`. The TypeScript types suggest entity fields
|
|
161
|
+
(`name`, `aliases`, `type`) exist at the top level. **They don't.** The
|
|
162
|
+
API wraps them in a `.report` container. The generated client does NOT
|
|
163
|
+
unwrap this automatically.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// WRONG — name will be undefined:
|
|
167
|
+
const res = await client.getNamedEntityReport(neid);
|
|
168
|
+
const name = res.name; // undefined!
|
|
169
|
+
|
|
170
|
+
// CORRECT — always access through .report:
|
|
171
|
+
const res = await client.getNamedEntityReport(neid);
|
|
172
|
+
const name = res.report?.name ?? (res as any).name ?? neid;
|
|
173
|
+
const aliases = res.report?.aliases ?? (res as any).aliases ?? [];
|
|
174
|
+
const type = res.report?.type ?? (res as any).type;
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `(res as any).name` fallback handles the case where the client is
|
|
178
|
+
eventually fixed to unwrap the response.
|
|
179
|
+
|
|
95
180
|
> **WARNING -- `getPropertyValues()` takes JSON-stringified arrays**: The `eids`
|
|
96
181
|
> and `pids` parameters must be JSON-encoded strings, NOT native arrays. The
|
|
97
182
|
> TypeScript type is `string`, not `string[]`. Passing a raw array will silently
|
|
@@ -114,6 +199,17 @@ financial instruments, events, and all other types are excluded — even
|
|
|
114
199
|
though the schema shows relationships like `filed` connecting organizations
|
|
115
200
|
to documents.
|
|
116
201
|
|
|
202
|
+
**Why?** The knowledge graph has two layers. The **graph layer** models
|
|
203
|
+
first-class entities (people, organizations, locations) as nodes with
|
|
204
|
+
edges between them — this is what `getLinkedEntities` traverses. The
|
|
205
|
+
**property layer** attaches everything else (documents, filings, financial
|
|
206
|
+
instruments, events) as property values on graph nodes. Documents aren't
|
|
207
|
+
"lesser" entities — they're stored differently because they're associated
|
|
208
|
+
with specific graph nodes rather than standing independently in the graph.
|
|
209
|
+
Understanding this distinction helps you generalize: if a target entity
|
|
210
|
+
type doesn't appear in the `getLinkedEntities` response, it's a property-
|
|
211
|
+
layer entity and you need `getPropertyValues` with the relationship PID.
|
|
212
|
+
|
|
117
213
|
To traverse relationships to non-graph-node types, use `getPropertyValues`
|
|
118
214
|
with the relationship PID instead. Relationship properties (`data_nindex`)
|
|
119
215
|
return linked entity IDs as values. Zero-pad the returned IDs to 20
|
|
@@ -139,6 +235,51 @@ See the **cookbook** rule for a full "Get filings for a company" recipe.
|
|
|
139
235
|
(`POST /elemental/find`). Best for filtered searches (by type, property,
|
|
140
236
|
relationship). See `find.md` for the expression language.
|
|
141
237
|
|
|
238
|
+
## Common Entity Relationships
|
|
239
|
+
|
|
240
|
+
The knowledge graph connects entities through relationship properties
|
|
241
|
+
(`data_nindex` PIDs). These are the most common patterns:
|
|
242
|
+
|
|
243
|
+
| From | PID | To | How to traverse |
|
|
244
|
+
|---|---|---|---|
|
|
245
|
+
| Organization | `filed` | Document (filings) | `getPropertyValues` with the `filed` PID on the org's NEID |
|
|
246
|
+
| Organization | `employs` | Person | `getLinkedEntities` (person is a graph node type) |
|
|
247
|
+
| Person | `employed_by` | Organization | `getLinkedEntities` (organization is a graph node type) |
|
|
248
|
+
| Organization | `headquartered_in` | Location | `getLinkedEntities` (location is a graph node type) |
|
|
249
|
+
| Any entity | `related_to` | Any entity | `getLinkedEntities` for person/org/location; `getPropertyValues` for others |
|
|
250
|
+
|
|
251
|
+
**Key constraint:** `getLinkedEntities` only works for three target types:
|
|
252
|
+
**person**, **organization**, **location**. For documents, filings,
|
|
253
|
+
articles, financial instruments, and events, use `getPropertyValues` with
|
|
254
|
+
the relationship PID. See the cookbook rule (recipe #7) for a full filings
|
|
255
|
+
example.
|
|
256
|
+
|
|
257
|
+
**Traversal pattern for non-graph-node types:**
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// 1. Get the PID for the relationship
|
|
261
|
+
const schema = await client.getSchema();
|
|
262
|
+
const pids = schema.schema?.properties ?? [];
|
|
263
|
+
const filedPid = pids.find((p: any) => p.name === 'filed')?.pid;
|
|
264
|
+
|
|
265
|
+
// 2. Get linked entity IDs via getPropertyValues
|
|
266
|
+
const res = await client.getPropertyValues({
|
|
267
|
+
eids: JSON.stringify([orgNeid]),
|
|
268
|
+
pids: JSON.stringify([filedPid]),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// 3. Pad IDs to 20 chars to form valid NEIDs
|
|
272
|
+
const docNeids = res.values.map((v: any) => String(v.value).padStart(20, '0'));
|
|
273
|
+
|
|
274
|
+
// 4. Get details for each linked entity (response is nested under .report)
|
|
275
|
+
const reports = await Promise.all(
|
|
276
|
+
docNeids.map(async (neid: string) => {
|
|
277
|
+
const r = await client.getNamedEntityReport(neid);
|
|
278
|
+
return r.report ?? r;
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
```
|
|
282
|
+
|
|
142
283
|
## Error Handling
|
|
143
284
|
|
|
144
285
|
```typescript
|
package/rules/architecture.mdc
CHANGED
|
@@ -133,9 +133,21 @@ Tenant-specific configuration generated during provisioning. Contains GCP projec
|
|
|
133
133
|
|
|
134
134
|
## Built-in Pages
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
- `pages/
|
|
140
|
-
|
|
136
|
+
Recent template versions include these pages. They can be kept, modified,
|
|
137
|
+
or removed based on the app's needs:
|
|
138
|
+
|
|
139
|
+
- `pages/chat.vue` -- Agent chat UI. Uses `useAgentChat()` and
|
|
140
|
+
`useTenantConfig()` to discover deployed agents and stream responses
|
|
141
|
+
through the Portal Gateway. Keep if the app uses AI agents.
|
|
142
|
+
- `pages/mcp.vue` -- MCP Explorer. Browse and test MCP server tools. Keep
|
|
143
|
+
if the app uses MCP servers.
|
|
144
|
+
- `pages/entity-lookup.vue` -- Entity search tool. Useful for looking up
|
|
145
|
+
NEIDs. Keep or remove based on the app.
|
|
146
|
+
|
|
147
|
+
**If these pages are missing** from your project, your project was created
|
|
148
|
+
from an older template version. Create them from scratch — they're
|
|
149
|
+
straightforward Vuetify pages. The key composables (`useAgentChat`,
|
|
150
|
+
`useTenantConfig`) are included in all template versions and provide the
|
|
151
|
+
connection logic. See the `agents` cursor rule for the gateway endpoints
|
|
152
|
+
and response formats these pages use.
|
|
141
153
|
|
package/rules/cookbook.mdc
CHANGED
|
@@ -467,7 +467,7 @@ NOT supported — use `getPropertyValues` with the relationship PID instead.
|
|
|
467
467
|
docNeids.map(async (neid: string) => {
|
|
468
468
|
try {
|
|
469
469
|
const r = await client.getNamedEntityReport(neid);
|
|
470
|
-
return r.name
|
|
470
|
+
return r.report?.name ?? (r as any).name ?? neid;
|
|
471
471
|
} catch {
|
|
472
472
|
return neid;
|
|
473
473
|
}
|