@yottagraph-app/aether-instructions 1.1.37 → 1.1.39

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottagraph-app/aether-instructions",
3
- "version": "1.1.37",
3
+ "version": "1.1.39",
4
4
  "description": "Cursor rules, commands, and skills for Aether development",
5
5
  "files": [
6
6
  "rules",
@@ -71,6 +71,45 @@ In production, all Elemental API calls go through the Portal Gateway at
71
71
  `broadchurch.yaml`) and the portal injects its own Auth0 M2M token
72
72
  upstream.
73
73
 
74
+ ## Agent secrets (DATABASE_URL etc.)
75
+
76
+ For runtime values that aren't shipped in `broadchurch.yaml` and can't be
77
+ sourced from Vercel — most commonly `DATABASE_URL` for tenants whose
78
+ Postgres is provisioned via the Vercel Neon integration — use
79
+ `get_agent_secret()` from `broadchurch_auth`. It fetches from the portal
80
+ at `GET /api/agent-secrets/{org_id}` using the same `X-Api-Key` the agent
81
+ already carries, and caches per-process.
82
+
83
+ ```python
84
+ try:
85
+ from broadchurch_auth import get_agent_secret
86
+ except ImportError:
87
+ from .broadchurch_auth import get_agent_secret
88
+
89
+ def my_db_tool() -> str:
90
+ db_url = get_agent_secret("DATABASE_URL")
91
+ if not db_url:
92
+ return "DATABASE_URL is not configured for this tenant."
93
+ # ... use db_url ...
94
+ ```
95
+
96
+ Resolution order is `os.environ[name]` → portal-stored secret → default.
97
+ The env var path means local dev "just works" with `export DATABASE_URL=...`
98
+ without touching the portal.
99
+
100
+ Set values via the portal admin endpoint:
101
+
102
+ ```bash
103
+ curl -X PUT $GATEWAY_URL/api/projects/$ORG_ID/agent-secrets \
104
+ -H 'Content-Type: application/json' \
105
+ -d '{"vars": {"DATABASE_URL": "postgresql://..."}}'
106
+ ```
107
+
108
+ Secrets are stored encrypted-at-rest in Firestore on the tenant doc.
109
+ There is no per-tenant Vercel/Neon integration — when credentials rotate,
110
+ push the new value via the same endpoint and let the agent cold-start
111
+ pick it up (or call `get_agent_secrets(refresh=True)`).
112
+
74
113
  ## MCP-based agents
75
114
 
76
115
  When the project uses **MCP-only data architecture**, agents access the
package/rules/data.mdc CHANGED
@@ -142,8 +142,8 @@ Elemental API patterns. **Use these instead of writing from scratch:**
142
142
  ```typescript
143
143
  const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
144
144
  await refresh(); // fetches once, then cached
145
- const articleFid = flavorByName('article'); // → number | null
146
- const namePid = pidByName('name'); // → typically 8
145
+ const articleFid = flavorByName('article'); // → string | null
146
+ const namePid = pidByName('name'); // → string | null
147
147
  ```
148
148
 
149
149
  Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
@@ -297,15 +297,31 @@ Same value, different key. Always use a fallback:
297
297
 
298
298
  ```typescript
299
299
  const articleFlavor = flavors.find(f => f.name === 'article');
300
- const articleFid = articleFlavor?.fid ?? articleFlavor?.findex ?? null;
300
+ // Always use String() safe for small IDs (12) and required for large ones
301
+ const articleFid = String(articleFlavor?.fid ?? articleFlavor?.findex ?? '');
301
302
 
302
303
  // When building a FID lookup map:
303
- const fidMap = new Map(flavors.map(f => [f.fid ?? f.findex, f.name]));
304
+ const fidMap = new Map(flavors.map(f => [String(f.fid ?? f.findex), f.name]));
304
305
  ```
305
306
 
306
307
  The `is_type` expression in `/elemental/find` always uses the `fid` key
307
308
  regardless of which schema endpoint provided the value.
308
309
 
310
+ ### Some FIDs and PIDs are 64-bit -- `JSON.parse` will silently corrupt them
311
+
312
+ FIDs and PIDs are stored as 64-bit signed integers. Many are small
313
+ (e.g. `12`), but others exceed JavaScript's `Number.MAX_SAFE_INTEGER`
314
+ (2^53 - 1) -- for example, `3466547124233281063`. `JSON.parse` silently
315
+ rounds these large values (that example becomes `3466547124233281000`). An `is_type` query with
316
+ the rounded FID returns empty results and no error, making it look like
317
+ the data doesn't exist.
318
+
319
+ **Always treat FIDs and PIDs as strings in TypeScript/JavaScript.**
320
+ Before `JSON.parse`, rewrite large numeric `fid`/`pid` fields to
321
+ quoted strings. Store them as `string`, not `number`. Build expressions
322
+ and `pids` arrays via string interpolation, not `JSON.stringify` of a
323
+ JS number. This is safe for small IDs too.
324
+
309
325
  ### Relationship property values need zero-padding to form valid NEIDs
310
326
 
311
327
  Relationship properties (`data_nindex`) return linked entity IDs as raw