aegis-platform-sdk 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,58 +1,58 @@
1
- {
2
- "name": "aegis-platform-sdk",
3
- "version": "1.0.0",
4
- "description": "AEGIS Ontology SDK — typed TypeScript client for the AEGIS HTTPS API (Safe-Core Technologies)",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "author": "Safe-Core Technologies",
7
- "type": "module",
8
- "main": "./dist/index.cjs",
9
- "module": "./dist/index.js",
10
- "types": "./dist/index.d.ts",
11
- "bin": {
12
- "aegis-osdk": "./dist/cli.js"
13
- },
14
- "exports": {
15
- ".": {
16
- "types": "./dist/index.d.ts",
17
- "import": "./dist/index.js",
18
- "require": "./dist/index.cjs"
19
- }
20
- },
21
- "files": [
22
- "dist",
23
- "skills",
24
- "README.md",
25
- "CHANGELOG.md",
26
- "LICENSE"
27
- ],
28
- "sideEffects": false,
29
- "engines": {
30
- "node": ">=18.0.0"
31
- },
32
- "scripts": {
33
- "build": "tsup",
34
- "typecheck": "tsc --noEmit",
35
- "test": "vitest run",
36
- "test:watch": "vitest",
37
- "docs": "typedoc src/index.ts --out docs/api",
38
- "prepublishOnly": "npm run typecheck && npm run test && npm run build"
39
- },
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/Safe-Core/aegis-typescript-sdk.git"
43
- },
44
- "keywords": [
45
- "aegis",
46
- "safe-core",
47
- "ontology",
48
- "sdk",
49
- "client"
50
- ],
51
- "devDependencies": {
52
- "@types/node": "^22.10.0",
53
- "tsup": "^8.3.5",
54
- "typedoc": "^0.28.20",
55
- "typescript": "^5.7.2",
56
- "vitest": "^3.0.0"
57
- }
58
- }
1
+ {
2
+ "name": "aegis-platform-sdk",
3
+ "version": "1.3.0",
4
+ "description": "AEGIS Ontology SDK — typed TypeScript client for the AEGIS HTTPS API (Safe-Core Technologies)",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "Safe-Core Technologies",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "bin": {
12
+ "aegis-osdk": "./dist/cli.js"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "skills",
24
+ "README.md",
25
+ "CHANGELOG.md",
26
+ "LICENSE"
27
+ ],
28
+ "sideEffects": false,
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup",
34
+ "typecheck": "tsc --noEmit",
35
+ "test": "vitest run",
36
+ "test:watch": "vitest",
37
+ "docs": "typedoc src/index.ts --out docs/api",
38
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/Safe-Core/aegis-typescript-sdk.git"
43
+ },
44
+ "keywords": [
45
+ "aegis",
46
+ "safe-core",
47
+ "ontology",
48
+ "sdk",
49
+ "client"
50
+ ],
51
+ "devDependencies": {
52
+ "@types/node": "^22.10.0",
53
+ "tsup": "^8.3.5",
54
+ "typedoc": "^0.28.20",
55
+ "typescript": "^5.7.2",
56
+ "vitest": "^3.0.0"
57
+ }
58
+ }
@@ -1,139 +1,140 @@
1
- ---
2
- name: aegis-sdk
3
- description: Use when writing TypeScript/JavaScript code that talks to an AEGIS platform deployment (Safe-Core) — auth, IAM, ontology objects, AIP flows/agents, Code Functions, datasets. Covers the aegis-platform-sdk npm package - client construction, auth flows, resource surface, error handling, and conventions.
4
- ---
5
-
6
- # AEGIS TypeScript SDK (`aegis-platform-sdk`)
7
-
8
- Typed, zero-dependency client for the AEGIS HTTPS API. Mirrors the Python
9
- SDK (`aegis-platform-sdk` on PyPI, import `aegis`) — same resources, same
10
- error hierarchy, same auth model.
11
-
12
- ## Rules
13
-
14
- 1. **Never hand-roll `fetch` calls against an AEGIS backend** in a project
15
- that has this SDK — construct one `AegisClient` and use its resources.
16
- For endpoints the SDK doesn't wrap yet, use the escape hatch
17
- `client.request(method, path, { params, json })` (it still gets auth +
18
- error mapping), and note the gap so it can be added to the SDK.
19
- 2. **Auth**: programmatic callers use an OSDK token (`osdk_…`, issued in
20
- the AEGIS UI under Governance → OSDK Applications), passed as
21
- `new AegisClient({ baseUrl, token })` or via env `AEGIS_API_URL` +
22
- `AEGIS_TOKEN`. Interactive flows call
23
- `await client.auth.login(username, password, { totpCode? })` — the JWT
24
- attaches to the client automatically.
25
- 3. **Never log or persist tokens.** `osdk.create()`/`osdk.rotate()` return
26
- the plaintext secret exactly once.
27
- 4. **Error handling**: catch the SDK hierarchy, not raw HTTP.
28
- `AuthError` (401) → re-auth; `PermissionDeniedError` (403) → surface the
29
- missing permission (`err.detail`); `NotFoundError` (404) → may be tenant
30
- scoping, not just absence; base `AegisAPIError` exposes
31
- `statusCode` / `detail` / `payload` (e.g. dataset merge conflicts arrive
32
- as 409 with the conflict body in `payload`).
33
- 5. **Everything is tenant-scoped** by the token's identity. Do not build
34
- cross-tenant logic client-side.
35
-
36
- ## Client construction
37
-
38
- ```ts
39
- import { AegisClient } from "aegis-platform-sdk";
40
-
41
- const client = new AegisClient({
42
- baseUrl: process.env.AEGIS_API_URL, // e.g. https://aegis.example.com
43
- token: process.env.AEGIS_TOKEN, // osdk_… or JWT; optional if using login()
44
- timeoutMs: 15_000, // default
45
- });
46
- ```
47
-
48
- Server-side only for secrets: in browser apps, never embed an `osdk_…`
49
- token — proxy through your backend or use the login flow.
50
-
51
- **Proxy mode** (cookie-auth frontends — e.g. a Next.js app whose `/api/*`
52
- routes inject the Bearer from an httpOnly cookie): relative `baseUrl` +
53
- `fetchInit`, no token in JS:
54
-
55
- ```ts
56
- export const aegis = new AegisClient({
57
- baseUrl: "/api",
58
- fetchInit: { credentials: "include", cache: "no-store" },
59
- });
60
- ```
61
-
62
- ## OSDK codegen (typed ontology objects)
63
-
64
- `npx aegis-osdk generate --lang typescript` (env `AEGIS_API_URL`/`AEGIS_TOKEN`
65
- or `--base-url`/`--token`) writes a typed module from the LIVE ontology —
66
- one interface per Object Type + a `Functions` class. Wire it with
67
- `asOsdkClient(client)`. Regenerate when the ontology contract changes
68
- (the file header carries the contract version). `npx aegis-osdk manifest`
69
- dumps the raw manifest JSON.
70
-
71
- ## Hardening (v1.0)
72
-
73
- - **Typed results, no casts**: every direct resource method takes a type
74
- parameter — `client.geo.nodes<GeoNodesOut>(opts)`, `client.ontology.objects.list<Camera>("Camera")`.
75
- Default `T = Json` when omitted.
76
- - **Retry**: transient failures (network/timeout/429/502/503/504) auto-retry
77
- with backoff on GET/HEAD (2 retries default). Tune via `retry` option;
78
- `{retries: 0}` disables; `retryPost: true` extends to writes (only when
79
- the endpoint is idempotent!).
80
- - **JWT refresh**: after `auth.login`, a 401 transparently rotates the
81
- refresh token (`POST /auth/refresh`) and retries once — no manual
82
- re-login loops needed. OSDK tokens (`osdk_…`) don't refresh — rotate via
83
- `client.osdk.rotate`.
84
-
85
- ## Resource surface (v1.0 — full parity with the Python SDK)
86
-
87
- - `client.auth` — `login`, `me` (WhoAmI: roles/permissions/tenant), `enableDevMode`
88
- - `client.iam` — `users.list/get`, `roles.list/get`, `groups` CRUD + members, `permissions.list(resource?)`, `nav.tree(asRoleId?)`
89
- - `client.osdk` — OSDK token lifecycle: `list/get/create(name, scopes, expiresAt?)/rotate/revoke`
90
- - `client.operator.tasks` — `list/get/create({ prompt_text, … })/cancel/retry/reprioritize`
91
- - `client.ontology`
92
- - `objects` — `get/list(kind, {limit, offset, q})/create(kind, properties, name?)/createMany/delete/bulkDelete/links/history/queryTimeseries/appendTimeseries`
93
- - `graph.expand(seedId, {depth, limit})`, `explorer.histogram/timeline`
94
- - `objectTypes` CRUD, `linkTypes.declare/delete`
95
- - `osdkManifest()/osdkFunctions()/osdkGenerate(lang?)` — typed codegen from the live ontology
96
- - `client.aip` — `flows` (list/get/runs/patch/run), `agents` (list/get/patch/manualRun/runs), `models` (catalog/defaults/setDefault), `budget` (get/set)
97
- - `client.functions` — Code Functions: CRUD, `invoke(slug, inputs)`, tests, `publish`, `versions`, pull-request lifecycle + CI
98
- - `client.codeRepositories` — repos, branches, CI, merge
99
- - `client.datasets` — `list/get/sample`, branches, transactions (`beginTransaction(name, "SNAPSHOT"|"APPEND"|"UPDATE"|"DELETE")` → `commitTransaction`/`abortTransaction`), `mergeFastForward`/`mergeThreeWay`, classification, markings
100
- - **Reading**: `client.geo` (nodes/nodeTypes/sources/edges/zones), `client.mapTemplates` (list/get/create/instantiate/exportSvg), `client.media` (sets + item metadata), `client.docs` (tree/read), `client.pages` (registry CRUD/publish), `client.dossier` (startGeneric/startComposite → poll `get`), `client.timeseries` (charts + render)
101
- - **Operational**: `client.alerts` (`feeds`/`watches`/`geofences`/`shares`/`routes` incl. silence/`inbox` + `escalate`/`channels`), `client.alarms` (threshold rules), `client.events` (pipelineStatus + DLQ retry/purge), `client.connectors` (list/patch/register/test/run/skillCatalog), `client.pipelines` (CRUD/build/runs/previews), `client.chat` (channels/messages), `client.notepad`
102
- - **Governance**: `client.lineage` (graph/events), `client.accessAudit`, `client.markings` (+`categories`, eligibility), `client.resourceMarkings`, `client.propertyMarkings`, `client.rowPolicies`, `client.erasure` (ALWAYS `preview` before `forget`; `forget` needs `confirm:true`), `client.retention`, `client.guestTokens` (issue returns plaintext once)
103
- - **Platform**: `client.workshop` (`briefings`/`dossiers`/`covs`/`widgetCatalog` — no apps/widgets CRUD, by design), `client.compute` (`providers`/`releases`/selectModel/visionCount + `control(providerId)` scoped plane), `client.inference` (complete/chat/chatWithRag/models/auditTrail — server-audited LLM), `client.codegen`, `client.correlation` (quickLinks/startChain→getChain→saveChain), `client.situational` (nearby/correlate/cut), `client.merge` (suggestTarget/preview/apply)
104
- - **Domain**: `client.atlas` (layers/KML/evaluator), `client.briefing`, `client.cctv` (streams/detections/recordings/exports/bookmarks/gcps/hotlist; `recordingsPlaylist` returns raw m3u8 text), `client.edge` (pairing codes/fleet version), `client.video`, `client.comunicados`, `client.campaign.briefing`
105
- - **Collaboration**: `client.forms` (governed submissions), `client.appShell` (banner/footer/userMenu/sidenav/homepage get/put), `client.groups`, `client.projects` (move + `constraints`), `client.organizations`, `client.spaces`, `client.solutions` (design→materialize→teardown; marketplace listings), `client.workspaces` (+`versions`, navigation, promotions, kiosk), `client.workspaceUpdates`, `client.marketplace` (storefront/install wizard)
106
- - **Versioned surfaces** (`client.platformV22`…`platformV26`): action-log/quiver/usage (v22); aip-logic/action-types/assist/automate/health/contour/vertex/repos/object-sets/styles incl. `styles.uploadLogo` multipart (v23); rules/checkpoints/expectations/change-requests/scenarios/searchAround/aggregateObjectSet (v24); machinery/branch-protection/kiosk/analyst/dataset rollback/freshness/code-scan/evals (v25); promotions/object-views/capacity/listeners/scanner/monitoring/health-checks/insight/peers/run-history (v26)
107
- - **SSE streaming**: `for await (const ev of client.stream(path, {params, json, signal}))` yields `{event, data, id?, retry?}`; pass `signal` to cancel (no default timeout). `parseSseStream` is exported for custom transports.
108
- - **Static catalogs (no HTTP)**: `NAV_PERMISSIONS`/`CAPABILITY_PERMISSIONS`/`ALL_PERMISSIONS` + `PermissionKey` union + `isSystemPermission()` — reference permission keys statically, never hardcode strings; `APPLICATIONS`/`applicationBySlug()` OSDK app permission scopes for `osdk.create`.
109
- - Client-side helpers (no HTTP): `maxLevel`, `validateInvariant`, `CLASSIFICATION_RANK` — validate the classification invariant before writes.
110
-
111
- ## Conventions
112
-
113
- - **Declare request-body shapes with `type`, not `interface`**: SDK input
114
- params are typed `Json` (`Record<string, unknown>`); TS gives object
115
- `type` aliases an implicit index signature (assignable to `Json`) but
116
- NOT interfaces an `interface` argument fails with "Index signature
117
- for type 'string' is missing". Result shapes can be either (they flow
118
- through the generic, not assignability).
119
-
120
- - Wire fields are `snake_case` (backend-shaped); SDK method names are
121
- `camelCase`. Request bodies you build use snake_case keys (e.g.
122
- `{ prompt_text, project_id }`).
123
- - Loosely-typed returns are `Json` (`Record<string, unknown>`) — same
124
- policy as the Python SDK's `dict`; they graduate to named interfaces as
125
- endpoints stabilize.
126
- - `ontology.objects.list(kind)` maps `kind` → query param `node_type`
127
- (AEGIS nodes carry `node_type` = ontology kind).
128
- - Datasets writes are transactional: begin → commit/abort. Never write
129
- without a transaction.
130
- - Prefer `AbortSignal` via `opts.signal` on `client.request` for
131
- cancellable calls; default is a per-request timeout.
132
-
133
- ## Extending the SDK itself
134
-
135
- The SDK repo is `Safe-Core/aegis-typescript-sdk` (private). Roadmap and
136
- parity plan live in `docs/PLAN.md` there. Every new AEGIS endpoint that
137
- external apps need should land as a resource method there (paired with the
138
- Python SDK see the repo plan), not as ad-hoc fetch code in the consuming
139
- app.
1
+ ---
2
+ name: aegis-sdk
3
+ description: Use when writing TypeScript/JavaScript code that talks to an AEGIS platform deployment (Safe-Core) — auth, IAM, ontology objects, AIP flows/agents, Code Functions, datasets. Covers the aegis-platform-sdk npm package - client construction, auth flows, resource surface, error handling, and conventions.
4
+ ---
5
+
6
+ # AEGIS TypeScript SDK (`aegis-platform-sdk`)
7
+
8
+ Typed, zero-dependency client for the AEGIS HTTPS API. Mirrors the Python
9
+ SDK (`aegis-platform-sdk` on PyPI, import `aegis`) — same resources, same
10
+ error hierarchy, same auth model.
11
+
12
+ ## Rules
13
+
14
+ 1. **Never hand-roll `fetch` calls against an AEGIS backend** in a project
15
+ that has this SDK — construct one `AegisClient` and use its resources.
16
+ For endpoints the SDK doesn't wrap yet, use the escape hatch
17
+ `client.request(method, path, { params, json })` (it still gets auth +
18
+ error mapping), and note the gap so it can be added to the SDK.
19
+ 2. **Auth**: programmatic callers use an OSDK token (`osdk_…`, issued in
20
+ the AEGIS UI under Governance → OSDK Applications), passed as
21
+ `new AegisClient({ baseUrl, token })` or via env `AEGIS_API_URL` +
22
+ `AEGIS_TOKEN`. Interactive flows call
23
+ `await client.auth.login(username, password, { totpCode? })` — the JWT
24
+ attaches to the client automatically.
25
+ 3. **Never log or persist tokens.** `osdk.create()`/`osdk.rotate()` return
26
+ the plaintext secret exactly once.
27
+ 4. **Error handling**: catch the SDK hierarchy, not raw HTTP.
28
+ `AuthError` (401) → re-auth; `PermissionDeniedError` (403) → surface the
29
+ missing permission (`err.detail`); `NotFoundError` (404) → may be tenant
30
+ scoping, not just absence; base `AegisAPIError` exposes
31
+ `statusCode` / `detail` / `payload` (e.g. dataset merge conflicts arrive
32
+ as 409 with the conflict body in `payload`).
33
+ 5. **Everything is tenant-scoped** by the token's identity. Do not build
34
+ cross-tenant logic client-side.
35
+
36
+ ## Client construction
37
+
38
+ ```ts
39
+ import { AegisClient } from "aegis-platform-sdk";
40
+
41
+ const client = new AegisClient({
42
+ baseUrl: process.env.AEGIS_API_URL, // e.g. https://aegis.example.com
43
+ token: process.env.AEGIS_TOKEN, // osdk_… or JWT; optional if using login()
44
+ timeoutMs: 15_000, // default
45
+ });
46
+ ```
47
+
48
+ Server-side only for secrets: in browser apps, never embed an `osdk_…`
49
+ token — proxy through your backend or use the login flow.
50
+
51
+ **Proxy mode** (cookie-auth frontends — e.g. a Next.js app whose `/api/*`
52
+ routes inject the Bearer from an httpOnly cookie): relative `baseUrl` +
53
+ `fetchInit`, no token in JS:
54
+
55
+ ```ts
56
+ export const aegis = new AegisClient({
57
+ baseUrl: "/api",
58
+ fetchInit: { credentials: "include", cache: "no-store" },
59
+ });
60
+ ```
61
+
62
+ ## OSDK codegen (typed ontology objects)
63
+
64
+ `npx aegis-osdk generate --lang typescript` (env `AEGIS_API_URL`/`AEGIS_TOKEN`
65
+ or `--base-url`/`--token`) writes a typed module from the LIVE ontology —
66
+ one interface per Object Type + a `Functions` class. Wire it with
67
+ `asOsdkClient(client)`. Regenerate when the ontology contract changes
68
+ (the file header carries the contract version). `npx aegis-osdk manifest`
69
+ dumps the raw manifest JSON.
70
+
71
+ ## Hardening (v1.0)
72
+
73
+ - **Typed results, no casts**: every direct resource method takes a type
74
+ parameter — `client.geo.nodes<GeoNodesOut>(opts)`, `client.ontology.objects.list<Camera>("Camera")`.
75
+ Default `T = Json` when omitted.
76
+ - **Retry**: transient failures (network/timeout/429/502/503/504) auto-retry
77
+ with backoff on GET/HEAD (2 retries default). Tune via `retry` option;
78
+ `{retries: 0}` disables; `retryPost: true` extends to writes (only when
79
+ the endpoint is idempotent!).
80
+ - **JWT refresh**: after `auth.login`, a 401 transparently rotates the
81
+ refresh token (`POST /auth/refresh`) and retries once — no manual
82
+ re-login loops needed. OSDK tokens (`osdk_…`) don't refresh — rotate via
83
+ `client.osdk.rotate`.
84
+
85
+ ## Resource surface (v1.0 — full parity with the Python SDK)
86
+
87
+ - `client.auth` — `login`, `me` (WhoAmI: roles/permissions/tenant), `enableDevMode`
88
+ - `client.iam` — `users.list/get`, `roles.list/get`, `groups` CRUD + members, `permissions.list(resource?)`, `nav.tree(asRoleId?)`
89
+ - `client.osdk` — OSDK token lifecycle: `list/get/create(name, scopes, expiresAt?)/rotate/revoke`
90
+ - `client.operator.tasks` — `list/get/create({ prompt_text, … })/cancel/retry/reprioritize`
91
+ - `client.ontology`
92
+ - `objects` — `get/list(kind, {limit, offset, q})/create(kind, properties, name?)/createMany/delete/bulkDelete/links/history/queryTimeseries/appendTimeseries`
93
+ - `graph.expand(seedId, {depth, limit})`, `explorer.histogram/timeline`
94
+ - `objectTypes` CRUD, `linkTypes.declare/delete`
95
+ - `osdkManifest()/osdkFunctions()/osdkGenerate(lang?)` — typed codegen from the live ontology
96
+ - `client.aip` — `flows` (list/get/runs/patch/run), `agents` (list/get/patch/manualRun/runs), `models` (catalog/defaults/setDefault), `budget` (get/set)
97
+ - `client.functions` — Code Functions: CRUD, `invoke(slug, inputs)`, tests, `publish`, `versions`, pull-request lifecycle + CI
98
+ - `client.codeRepositories` — repos, branches, CI, merge
99
+ - `client.datasets` — `list/get/sample`, branches, transactions (`beginTransaction(name, "SNAPSHOT"|"APPEND"|"UPDATE"|"DELETE")` → `commitTransaction`/`abortTransaction`), `mergeFastForward`/`mergeThreeWay`, classification, markings
100
+ - **Reading**: `client.geo` (nodes/nodeTypes/sources/edges/zones), `client.mapTemplates` (list/get/create/instantiate/exportSvg), `client.media` (sets + item metadata), `client.docs` (tree/read), `client.pages` (registry CRUD/publish), `client.dossier` (startGeneric/startComposite → poll `get`), `client.timeseries` (charts + render)
101
+ - **Public guest** (anonymous, token in path — build `new AegisClient({ baseUrl })` with NO token): `client.publicGuest` — `menu(token)`, `fix(token, {lat,lng})` (GPS → evaluate_fix), `invoke(token, actionId, input)` (e.g. open gate; 403 geofence-gate / 429 throttle), `position(token)` (resident reads the visitor's live location — operator issues the resident token with `geo.live_ref` = visitor token id; poll it, no push), `document`/`queryObjectSet`/`documentNode`, `page`/`evaluatePage`, `mediaUrl(token, key)` (string for `<img src>`, no request). Visita finalizada → 410. Counterpart to operator-side `client.guestTokens`.
102
+ - **Operational**: `client.alerts` (`feeds`/`watches`/`geofences`/`shares`/`routes` incl. silence/`inbox` + `escalate`/`channels`), `client.alarms` (threshold rules), `client.events` (pipelineStatus + DLQ retry/purge), `client.connectors` (list/patch/register/test/run/skillCatalog), `client.pipelines` (CRUD/build/runs/previews), `client.chat` (channels/messages), `client.notepad`
103
+ - **Governance**: `client.lineage` (graph/events), `client.accessAudit`, `client.markings` (+`categories`, eligibility), `client.resourceMarkings`, `client.propertyMarkings`, `client.rowPolicies`, `client.erasure` (ALWAYS `preview` before `forget`; `forget` needs `confirm:true`), `client.retention`, `client.guestTokens` (issue returns plaintext once)
104
+ - **Platform**: `client.workshop` (`briefings`/`dossiers`/`covs`/`widgetCatalog` — no apps/widgets CRUD, by design), `client.compute` (`providers`/`releases`/selectModel/visionCount + `control(providerId)` scoped plane), `client.inference` (complete/chat/chatWithRag/models/auditTrail — server-audited LLM), `client.codegen`, `client.correlation` (quickLinks/startChain→getChain→saveChain), `client.situational` (nearby/correlate/cut), `client.merge` (suggestTarget/preview/apply)
105
+ - **Domain**: `client.atlas` (layers/KML/evaluator), `client.briefing`, `client.cctv` (streams/detections/recordings/exports/bookmarks/gcps/hotlist; `recordingsPlaylist` returns raw m3u8 text), `client.edge` (pairing codes/fleet version), `client.video`, `client.comunicados`, `client.campaign.briefing`
106
+ - **Collaboration**: `client.forms` (governed submissions), `client.appShell` (banner/footer/userMenu/sidenav/homepage get/put), `client.groups`, `client.projects` (move + `constraints`), `client.organizations`, `client.spaces`, `client.solutions` (design→materialize→teardown; marketplace listings), `client.workspaces` (+`versions`, navigation, promotions, kiosk), `client.workspaceUpdates`, `client.marketplace` (storefront/install wizard)
107
+ - **Versioned surfaces** (`client.platformV22`…`platformV26`): action-log/quiver/usage (v22); aip-logic/action-types/assist/automate/health/contour/vertex/repos/object-sets/styles incl. `styles.uploadLogo` multipart (v23); rules/checkpoints/expectations/change-requests/scenarios/searchAround/aggregateObjectSet (v24); machinery/branch-protection/kiosk/analyst/dataset rollback/freshness/code-scan/evals (v25); promotions/object-views/capacity/listeners/scanner/monitoring/health-checks/insight/peers/run-history (v26)
108
+ - **SSE streaming**: `for await (const ev of client.stream(path, {params, json, signal}))` — yields `{event, data, id?, retry?}`; pass `signal` to cancel (no default timeout). `parseSseStream` is exported for custom transports.
109
+ - **Static catalogs (no HTTP)**: `NAV_PERMISSIONS`/`CAPABILITY_PERMISSIONS`/`ALL_PERMISSIONS` + `PermissionKey` union + `isSystemPermission()` — reference permission keys statically, never hardcode strings; `APPLICATIONS`/`applicationBySlug()` — OSDK app permission scopes for `osdk.create`.
110
+ - Client-side helpers (no HTTP): `maxLevel`, `validateInvariant`, `CLASSIFICATION_RANK` — validate the classification invariant before writes.
111
+
112
+ ## Conventions
113
+
114
+ - **Declare request-body shapes with `type`, not `interface`**: SDK input
115
+ params are typed `Json` (`Record<string, unknown>`); TS gives object
116
+ `type` aliases an implicit index signature (assignable to `Json`) but
117
+ NOT interfaces an `interface` argument fails with "Index signature
118
+ for type 'string' is missing". Result shapes can be either (they flow
119
+ through the generic, not assignability).
120
+
121
+ - Wire fields are `snake_case` (backend-shaped); SDK method names are
122
+ `camelCase`. Request bodies you build use snake_case keys (e.g.
123
+ `{ prompt_text, project_id }`).
124
+ - Loosely-typed returns are `Json` (`Record<string, unknown>`) same
125
+ policy as the Python SDK's `dict`; they graduate to named interfaces as
126
+ endpoints stabilize.
127
+ - `ontology.objects.list(kind)` maps `kind` query param `node_type`
128
+ (AEGIS nodes carry `node_type` = ontology kind).
129
+ - Datasets writes are transactional: begin → commit/abort. Never write
130
+ without a transaction.
131
+ - Prefer `AbortSignal` via `opts.signal` on `client.request` for
132
+ cancellable calls; default is a per-request timeout.
133
+
134
+ ## Extending the SDK itself
135
+
136
+ The SDK repo is `Safe-Core/aegis-typescript-sdk` (private). Roadmap and
137
+ parity plan live in `docs/PLAN.md` there. Every new AEGIS endpoint that
138
+ external apps need should land as a resource method there (paired with the
139
+ Python SDK — see the repo plan), not as ad-hoc fetch code in the consuming
140
+ app.