Repository navigation
Fix types (breaking change) #11
Description
Type the remaining 23 client methods that return Promise<unknown>
Summary
23 public methods on Seclai return Promise<unknown> even though the OpenAPI
spec declares a concrete response shape for every one of them. Consumers have to
cast at every call site, and because unknown accepts any assertion, a wrong cast
compiles silently.
This is the remainder of the cleanup started in #10, which fixed the two new
methods (getGenerationTiers(), searchDocs()) and the one method that was
discarding a real $ref schema (updateOrganizationAlertPreference()). What's
left is a coordinated breaking change and needs a major release.
Why this needs #major
Widening unknown to any concrete type breaks the most likely consumer pattern.
Today the only way to use these methods is to cast, and unknown as T always
compiles. After the change it may not:
// before — compiles
const alerts = (await client.listAlerts()) as AlertList;
// after — TS2352
// Conversion of type 'Record<string, unknown>' to type 'AlertList' may be a
// mistake because neither type sufficiently overlaps with the other.
// If this was intentional, convert the expression to 'unknown' first.Casting to a type alias with an index signature still works, and
property-access-then-cast still works. It is specifically as SomeInterface that
breaks — and that is the pattern the current unknown return actively pushes
people toward.
So: land this behind #major in the merge commit, and call it out in the
changelog under **Breaking:**.
Scope
Type each method from its actual spec response. Do not apply
Record<string, unknown> uniformly — four of these have a different shape and
Record would be wrong.
Free-form object → Record<string, unknown> (19)
Spec response is {"type": "object", "additionalProperties": true}.
| Method | client.ts | Endpoint |
|---|---|---|
listAlerts |
L2443 | GET /alerts |
getAlert |
L2455 | GET /alerts/{alert_id} |
changeAlertStatus |
L2465 | POST /alerts/{alert_id}/status |
addAlertComment |
L2475 | POST /alerts/{alert_id}/comments |
subscribeToAlert |
L2484 | POST /alerts/{alert_id}/subscribe |
unsubscribeFromAlert |
L2493 | POST /alerts/{alert_id}/unsubscribe |
listAlertConfigs |
L2505 | GET /alerts/configs |
createAlertConfig |
L2517 | POST /alerts/configs |
getAlertConfig |
L2527 | GET /alerts/configs/{config_id} |
updateAlertConfig |
L2538 | PATCH /alerts/configs/{config_id} |
listModelAlerts |
L2588 | GET /models/alerts |
getUnreadModelAlertCount |
L2604 | GET /models/alerts/unread-count |
getModelRecommendations |
L2622 | GET /models/{model_id}/recommendations |
listExperiments |
L2684 | GET /models/playground/experiments |
createExperiment |
L2695 | POST /models/playground/experiments |
getExperiment |
L2704 | GET /models/playground/experiments/{experiment_id} |
cancelExperiment |
L2713 | POST /models/playground/experiments/{experiment_id}/cancel |
getMemoryBankStats |
L1814 | GET /memory_banks/{memory_bank_id}/stats |
search |
L2745 | GET /search |
Array → unknown[] (2)
Record<string, unknown> would be incorrect here.
| Method | client.ts | Endpoint |
|---|---|---|
getAgentsUsingMemoryBank |
L1805 | GET /memory_banks/{memory_bank_id}/agents |
listMemoryBankTemplates |
L1858 | GET /memory_banks/templates |
Map of boolean → Record<string, boolean> (2)
Spec response is {"additionalProperties": {"type": "boolean"}}.
| Method | client.ts | Endpoint |
|---|---|---|
acceptMemoryBankAiSuggestion |
L1887 | PATCH /memory_banks/ai-assistant/{conversation_id} |
acceptAiMemoryBankSuggestion |
L2983 | PATCH /ai-assistant/memory-bank/{conversation_id} |
Out of scope
- The private
uploadFile()helper (L607) — internal, not public API. - Already fixed in Updated the SDK for email agent triggers and email domain management … #10:
getGenerationTiers(),searchDocs(),
updateOrganizationAlertPreference().
Worth considering while in here
Many of the 19 "free-form object" responses are almost certainly modelled
server-side and only serialise as untyped maps because the endpoint lacks a
response_model. Record<string, unknown> is an improvement over unknown, but
it is still not a real type. Before doing the mechanical pass, it's worth asking
whether the API can declare proper schemas for the alerts and playground
endpoints — that would make this a one-time break rather than two.
If the schemas do land, prefer the generated types over Record<string, unknown>
and update this issue accordingly.
Acceptance criteria
- Every method above returns the type matching its spec response
- No public method returns
Promise<unknown>;grep -c "Promise<unknown>" src/client.tsis 1 (the private helper) - Tests assert typed field access without a cast, so a regression fails the build
-
npm run typecheck && npm test && npm run buildpass - README examples that call these methods compile — paste into a scratch
.ts
importing from../src/index;npm run typecheckdoes not cover README -
CHANGELOG.mdhas a**Breaking:**entry under### Changed - The merge commit contains
#major
Sibling SDKs
The same pattern likely exists in seclai-python, seclai-go, seclai-csharp,
seclai-cli, and seclai-mcp — all generated from this spec. Worth running the
equivalent audit there and filing companions, so the family stays consistent
rather than diverging at each major.
Activity