@mitralab.io/platform-sdk 1.0.9 → 1.1.0-beta.1

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/CHANGELOG.md CHANGED
@@ -2,8 +2,40 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
- ## Unreleased
5
+ ## 1.1.0-beta.0
6
6
 
7
+ - Complete native Function sync, async, polling, cancellation, and anonymous public execution.
8
+ - Expose browser-safe Agent Tasks, restricted Agent Credentials, and model discovery through Core 0.2 contracts.
9
+ - Compose the Core-owned Agent task session manager with Platform WebSocket and HTTP/SSE adapters.
10
+ - Add native anonymous polling for executions created by the public async Function route.
11
+ - Keep business-Agent administration out of the browser adapter according to the app-role permission matrix.
12
+ - Expose app-scoped integration config listing and execution by alias from Core 0.2.
13
+ - Document the missing producer contract for native record selection by `jdbcConnectionConfigId` instead of inventing a browser-side translation.
14
+ - Document that entity `update` already implements the producer's partial PUT semantics, so no duplicate PATCH method is needed.
15
+ - Derive Function execution, integration proxy input, and custom query result types directly from Core without narrowing nullable or producer-returned fields.
16
+ - Preserve the complete Data Manager record envelope and execute Custom Queries with only
17
+ producer parameters, without a caller-selected Data Source or an `init()` precondition.
18
+
19
+ - Refresh app sessions proactively through IAM before authenticated native requests, with a 30-second JWT expiry heuristic and one shared refresh flight.
20
+ - Preserve sessions on transient refresh failures, clear them on definitive IAM client failures, and retain the one-time reactive `401` retry.
21
+ - Rotate both tokens without fetching the current user or notifying public auth-state listeners, while keeping the legacy bridge synchronized.
22
+ - Reject decodable access and refresh tokens whose app scope is missing or differs from the configured app while keeping opaque tokens server-authoritative.
23
+ - Fence refresh responses by session generation so late success or failure cannot undo sign-out or overwrite a newer login or bridged session.
24
+ - Bind reactive `401` handling to the token used by the rejected request so an old response cannot refresh or clear a replacement session.
25
+ - Redact values under sensitive credential field names from recursive API error details.
26
+ - Preserve the retained session when `auth.me()` reaches `401` after transient proactive and reactive refresh failures.
27
+ - Add native Google SSO through popup and redirect flows with direct IAM code exchange.
28
+ - Validate Google SSO origin, popup source, one-time state, cancellation, timeout, and token response shape.
29
+ - Require redirect errors to bind to the stored state before exposing or consuming them.
30
+ - Keep Google options limited to popup or redirect mode; account creation and locale remain producer concerns.
31
+ - Leave legacy-only `returnTo` and `title` on the deprecated aliases because the old runtime did not implement them as native Google controls.
32
+ - Preserve the email/password methods already public in Platform SDK 1.0.9 without presenting them as the new template flow.
33
+ - Route deprecated calls and legacy authentication through `${apiUrl}/legacy`.
34
+ - Apply the native auth page URL precedence to the deprecated SSO bridge.
35
+ - Propagate native sign-in, refresh, token changes, and sign-out to the legacy SDK session.
36
+ - Re-export the deprecated `mitra-interactions-sdk` surface from the package entrypoint.
37
+ - Mark every legacy type alias as deprecated in generated declarations.
38
+ - Share one session between this SDK and the legacy SDK in both directions.
7
39
  - Make the SonarCloud job wait for the Quality Gate result.
8
40
  - Align the public package metadata and ESM, CommonJS, and TypeScript artifacts.
9
41
  - Add package shape checks and public tarball smoke coverage.
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mitra-platform-sdk&metric=alert_status&token=28d7be14b66d6f88d706347e2418af5ea39ab3e9)](https://sonarcloud.io/summary/new_code?id=mitra-platform-sdk)
4
4
  [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mitra-platform-sdk&metric=coverage&token=28d7be14b66d6f88d706347e2418af5ea39ab3e9)](https://sonarcloud.io/summary/new_code?id=mitra-platform-sdk)
5
5
 
6
- JavaScript and TypeScript SDK for browser applications built on the Mitra Platform. Applications generated by Code Studio use this package to authenticate users, access Data Manager entities, execute Server Functions and custom queries, and call integrations.
6
+ JavaScript and TypeScript SDK for browser applications built on the Mitra Platform. Applications generated by Code Studio use this package to authenticate users, access Data Manager entities, execute Server Functions and custom queries, call integrations, and run live Agent tasks.
7
7
 
8
- The browser transport uses standard Web APIs only: `fetch`, `localStorage`, `URL`, and `Proxy`. Shared contracts and API modules come from `@mitralab.io/sdk-core`, without bringing browser authentication into the Core package.
8
+ The browser transport uses standard Web APIs only, including `fetch`, `WebSocket`, `ReadableStream`, `AbortController`, browser storage, `URL`, `Proxy`, `crypto`, and `atob`. Shared contracts and API modules come from `@mitralab.io/sdk-core`, without bringing browser authentication into the Core package.
9
9
 
10
10
  ## Installation
11
11
 
@@ -29,7 +29,11 @@ export const mitra = createClient({
29
29
  await mitra.init()
30
30
  ```
31
31
 
32
- `init()` resolves the application's public Code Studio configuration, including `dataSourceId` and `allowSignup`. Call it during application startup before using entities, custom queries, or sign-up.
32
+ `init()` resolves the application's public Code Studio configuration, including nullable
33
+ `dataSourceId` and `allowSignup`. The Data Source value remains part of the Platform 1.x
34
+ compatibility flow; native Entities and Custom Queries resolve the current app through the
35
+ authenticated request. Call `init()` during application startup before the compatibility sign-up
36
+ method needs `allowSignup`.
33
37
 
34
38
  ## Configuration
35
39
 
@@ -37,29 +41,30 @@ await mitra.init()
37
41
  |---|---|---|
38
42
  | `appId` | yes | ID of the published Code Studio application. |
39
43
  | `apiUrl` | yes | Base URL of the Mitra API gateway. |
44
+ | `authPageUrl` | no | Absolute URL of `sdk-auth.html`. Falls back to `window.__mitraEnv.authPageUrl`, then `/sdk-auth.html` on the `apiUrl` origin. |
40
45
  | `onError` | no | Global callback for API errors. |
41
46
 
42
- The client derives service endpoints from `apiUrl`: `/iam`, `/data-manager`, `/functions`, `/integration`, and `/code-studio`.
47
+ The client derives service endpoints from `apiUrl`: `/iam`, `/data-manager`, `/functions`, `/integration`, `/copilot`, and `/code-studio`.
43
48
 
44
49
  ## Boundary
45
50
 
46
51
  The Platform SDK owns:
47
52
 
48
- - browser login, sign-up, logout, and session refresh
53
+ - browser Google SSO, logout, and session refresh
54
+ - trusted session adoption for the embedded app preview
49
55
  - session persistence in `localStorage`
50
56
  - auth-state listeners
51
- - one retry after a successful token refresh on a `401` response
57
+ - proactive token refresh before authenticated native requests
58
+ - one retry after reactive `401` recovery with the current session
52
59
  - browser HTTP transport and public application initialization
60
+ - Agent WebSocket and HTTP/SSE live channels
53
61
 
54
- `@mitralab.io/sdk-core` owns the shared entities, custom queries, Functions, integrations, `auth.me`, safe paths, and structural response validation. Server Function code should use `@mitralab.io/functions-sdk` instead of this browser SDK.
62
+ `@mitralab.io/sdk-core` owns the shared entities, custom queries, Functions, integrations, Agent task lifecycle, `auth.me`, safe paths, and structural response validation. Server Function code should use `@mitralab.io/functions-sdk` instead of this browser SDK.
55
63
 
56
64
  ## Authentication
57
65
 
58
66
  ```typescript
59
- const user = await mitra.auth.signIn({
60
- email: "user@example.com",
61
- password: "password123",
62
- })
67
+ const user = await mitra.auth.signInWithGoogle({ mode: "popup" })
63
68
 
64
69
  const unsubscribe = mitra.auth.onAuthStateChange((currentUser) => {
65
70
  console.log(currentUser?.email)
@@ -69,7 +74,65 @@ mitra.auth.signOut("/login")
69
74
  unsubscribe()
70
75
  ```
71
76
 
72
- Authentication state is stored under `mitra_auth_{appId}`. When an API request returns `401`, the SDK attempts `refreshSession()` once and repeats the request only when refresh succeeds.
77
+ Authentication state is stored under `mitra_auth_{appId}`. Before each authenticated native request, the SDK checks a JWT's `exp` claim with a 30-second safety window and refreshes directly through IAM when needed. Opaque tokens, malformed JWTs, and JWTs without a numeric `exp` remain server-authoritative and proceed to the request. A `401` still triggers reactive recovery and at most one retry. If another login or bridged session replaced the token while the request was in flight, the retry uses that current token without refreshing its session. If sign-out cleared the token, the old `401` neither refreshes nor retries.
78
+
79
+ The generated-application authentication flow is Google SSO. The old native `signIn` and `signUp` names fail locally with `UNSUPPORTED_AUTH_METHOD` because IAM has no email/password endpoints. Deprecated login bindings remain available only through the legacy reexports.
80
+
81
+ An embedded preview can adopt the app-scoped session it receives from the platform without exchanging it:
82
+
83
+ ```typescript
84
+ mitra.auth.setSession({ accessToken, refreshToken })
85
+ await mitra.auth.checkAuth()
86
+ ```
87
+
88
+ Proactive and reactive callers share one in-flight refresh. Successful refresh rotates and persists both tokens and updates the legacy session bridge without calling `auth.me()` or notifying public auth-state listeners. A refresh response that arrives after sign-out or after another login/session replacement is discarded and cannot restore or overwrite that newer state. Network failures, `408`, `429`, and `5xx` responses preserve the current session; the original HTTP request proceeds with the current token so a later `401` can use the reactive fallback. `auth.me()` also preserves that retained session when the fallback refresh is transient. Other IAM `4xx` responses are definitive and clear the session.
89
+
90
+ JWT decoding is not user authentication. It schedules refresh and prevents cross-app session adoption. Every decodable access and refresh token must contain `app_id` exactly equal to the client's configured `appId`; opaque tokens are kept for rollout compatibility and remain validated by the server.
91
+
92
+ Custom WebSocket or Server-Sent Events boundaries can refresh explicitly before connecting:
93
+
94
+ ```typescript
95
+ const fresh = await mitra.auth.ensureFreshSession(30_000)
96
+
97
+ if (!fresh) {
98
+ // A required refresh failed. The SDK may still retain the session after a
99
+ // transient failure, but this boundary can choose whether to connect.
100
+ }
101
+ ```
102
+
103
+ `ensureFreshSession()` returns `true` when the token does not need refresh or refresh succeeds. It returns `false` when a required refresh fails, including a transient failure that intentionally preserves the current session.
104
+
105
+ Google SSO uses a popup by default. The SDK opens `sdk-auth.html`, validates the popup source, origin, and one-time state, exchanges the returned code directly with IAM, stores both tokens, and fetches the current user:
106
+
107
+ ```typescript
108
+ const user = await mitra.auth.signInWithGoogle({ mode: "popup" })
109
+ ```
110
+
111
+ The public Google options contain only `mode`. Account creation and locale are producer concerns, so the SDK does not send `create` or `language` to IAM. During rollout, the popup also accepts the older auth page token response.
112
+
113
+ Deprecated `LoginOptions` still preserve `returnTo` and `title` for source compatibility. They are not copied into the new Google API: the legacy runtime never read `title`, did not pass caller `returnTo` into popup login, and hardcoded the current URL for redirect login.
114
+
115
+ Redirect mode stores the one-time state and options in `sessionStorage`. Complete it during application startup before rendering authenticated routes:
116
+
117
+ ```typescript
118
+ const redirectedUser = await mitra.auth.completeGoogleSignInRedirect()
119
+
120
+ if (!redirectedUser) {
121
+ await mitra.auth.signInWithGoogle({ mode: "redirect" })
122
+ }
123
+ ```
124
+
125
+ Redirect errors are accepted only when `stateMitra` matches the stored one-time state. A missing or mismatched state leaves the fragment and redirect context untouched and does not expose `errorMitra`. The current alpha `sdk-auth.html` error redirect omits `stateMitra`, so those error redirects are intentionally rejected until that producer echoes the state; popup errors already carry state and are unaffected.
126
+
127
+ Configure the auth page explicitly when it is hosted outside the API gateway origin:
128
+
129
+ ```typescript
130
+ const mitra = createClient({
131
+ appId,
132
+ apiUrl,
133
+ authPageUrl: "https://app.example.com/sdk-auth.html",
134
+ })
135
+ ```
73
136
 
74
137
  ## Entities
75
138
 
@@ -80,7 +143,7 @@ type Task = {
80
143
  status: "pending" | "done"
81
144
  }
82
145
 
83
- const tasks = await mitra.entities.getTable<Task>("Task").list({
146
+ const { data: tasks } = await mitra.entities.getTable<Task>("Task").list({
84
147
  sort: "-created_at",
85
148
  limit: 10,
86
149
  fields: ["id", "title", "status"],
@@ -94,6 +157,10 @@ await mitra.entities.Task.delete(created.id)
94
157
 
95
158
  Table names are case-sensitive and must match the Data Manager table name. Record operations use `/api/v1/tables/{table}/records`. Application and tenant scope come from the authenticated context, not from a data source in the path.
96
159
 
160
+ The native producer does not accept `jdbcConnectionConfigId` or `dataSourceId` on public record requests. The deprecated record helpers still expose their legacy arguments, but there is no native equivalent until Data Manager defines an app-safe backend contract. The SDK does not translate those arguments into query parameters or SQL.
161
+
162
+ `update(id, fields)` sends the Data Manager PUT contract, which applies the supplied fields as a partial record update and preserves omitted fields. A separate `patch` alias would duplicate that producer behavior, so the native surface keeps one method.
163
+
97
164
  ## Server Functions
98
165
 
99
166
  ```typescript
@@ -104,7 +171,62 @@ const execution = await mitra.functions.execute("function-id", {
104
171
  console.log(execution.id, execution.status)
105
172
  ```
106
173
 
107
- The Platform SDK 1.x `execute` method keeps the existing asynchronous API behavior. It does not send `X-Invocation-Type`, so the Functions service applies its default and returns the created execution, normally with `PENDING` status.
174
+ `execute` sends `X-Invocation-Type: sync` and waits for the terminal result. `executeAsync` sends `async` and returns the initial execution for polling or cancellation.
175
+
176
+ The complete native lifecycle is also available. `getExecution` is kept because the Functions producer exposes execution polling and Agent consumers use it:
177
+
178
+ ```typescript
179
+ const queued = await mitra.functions.executeAsync("function-id", { orderId: "order-123" })
180
+ const current = await mitra.functions.getExecution(queued.id)
181
+ await mitra.functions.cancelExecution(current.id)
182
+ ```
183
+
184
+ Public Functions use a separate anonymous transport. It never adds `Authorization` or `X-App-Id`:
185
+
186
+ ```typescript
187
+ const result = await mitra.publicFunctions.execute("public-function-id", { sku: "A-1" })
188
+ const queuedPublic = await mitra.publicFunctions.executeAsync("public-function-id", { sku: "A-1" })
189
+ ```
190
+
191
+ Public async execution is fire-and-forget. The public API does not expose anonymous polling or
192
+ cancellation. Use synchronous `publicFunctions.execute` when the screen needs the result, or the
193
+ authenticated `functions.executeAsync` plus `functions.getExecution` flow after login.
194
+
195
+ ## Agent tasks and credentials
196
+
197
+ The browser-safe Copilot modules call the native service directly. `agentTasks` provides list, read, create, rename, archive, HTTP input, history, and live sessions. These direct primitives match the Copilot producer and MCP contract. Core owns session state, queueing, recovery, and reconciliation; Platform supplies only authenticated browser WebSocket/SSE channels. `agentCredentials` provides safe credential status, model discovery, API key, OAuth, and device authorization flows. Raw credentials are write-only.
198
+
199
+ ```typescript
200
+ const credentials = await mitra.agentCredentials.list()
201
+ const models = await mitra.agentCredentials.listModels()
202
+ const chats = await mitra.agentTasks.list({ archived: false, size: 20 })
203
+ ```
204
+
205
+ Create a live task lazily on the first message:
206
+
207
+ ```typescript
208
+ const session = mitra.agentTasks.session({
209
+ create: true,
210
+ agentType: models[0].agentType,
211
+ reasoningEffort: models[0].reasoningOptions[0],
212
+ })
213
+
214
+ const unsubscribe = session.on("delta", ({ delta, kind }) => {
215
+ console.log(kind, delta)
216
+ })
217
+
218
+ session.send("Analyze this application")
219
+ session.respondApproval(true)
220
+ await session.cancel()
221
+ unsubscribe()
222
+ session.close()
223
+ ```
224
+
225
+ Open an existing task with `session({ taskId })`. The default `auto` transport refreshes before connecting, opens `/copilot/ws/tasks/{taskId}`, and performs at most one safe recovery through persisted history plus the HTTP/SSE channel. Set `transport: "http"` when WebSockets are unavailable. Messages sent during a turn enter a FIFO queue with a maximum of 10 items; the session also exposes edit, remove, clear, approval, cancel, history, close, and typed events.
226
+
227
+ API keys and removal accept `ANTHROPIC` or `OPENAI`. OAuth accepts only `ANTHROPIC`; device authorization accepts only `OPENAI`. The facade enforces those producer-supported pairs in TypeScript and at runtime.
228
+
229
+ The browser token roles expose Agent tasks, credential status, and model discovery. Administrative business-Agent CRUD is intentionally not exposed by this adapter because the `USE` app role does not carry `AGENT_*` authority.
108
230
 
109
231
  ## Custom queries
110
232
 
@@ -116,8 +238,20 @@ const result = await mitra.queries.execute("query-id", {
116
238
  console.log(result.rows, result.affectedRows)
117
239
  ```
118
240
 
241
+ Custom Query execution sends only `parameters`. Data Manager resolves its Data Source from the
242
+ authenticated app, so Queries work without a caller-selected `dataSourceId` and do not depend on
243
+ `init()`.
244
+
119
245
  ## Integrations
120
246
 
247
+ List the current app's saved configs without exposing the Core admin module:
248
+
249
+ ```typescript
250
+ const configs = await mitra.integration.list({ page: 0, size: 20, sort: "alias,asc" })
251
+ ```
252
+
253
+ The Integration producer derives the app from the authenticated token, so this list is app-scoped.
254
+
121
255
  Execute a predefined resource:
122
256
 
123
257
  ```typescript
@@ -139,8 +273,41 @@ const result = await mitra.integration.execute("config-id", {
139
273
  console.log(result.status, result.body)
140
274
  ```
141
275
 
276
+ An app can also address a saved config by its app-scoped alias:
277
+
278
+ ```typescript
279
+ const result = await mitra.integration.executeByAlias("billing", {
280
+ method: "POST",
281
+ endpoint: "/invoices",
282
+ body: { customerId: "customer-1" },
283
+ })
284
+ ```
285
+
142
286
  Integration credentials are injected by the Integration service. Do not pass provider credentials through browser input.
143
287
 
288
+ ## Legacy surface
289
+
290
+ The public surface of `mitra-interactions-sdk` is re-exported from this package so an application can replace the legacy dependency without rewriting its call sites. Runtime re-exports are marked `@deprecated` and name their replacement, or state that no replacement exists yet. Every legacy type is an identity-preserving alias marked as a deprecated compatibility type.
291
+
292
+ ```typescript
293
+ import { createClient, loginWithGoogleMitra } from "@mitralab.io/platform-sdk"
294
+
295
+ export const mitra = createClient({ appId, apiUrl })
296
+
297
+ await loginWithGoogleMitra()
298
+ console.log(mitra.auth.accessToken)
299
+ ```
300
+
301
+ Google SSO is available through `mitra.auth.signInWithGoogle` and `mitra.auth.completeGoogleSignInRedirect`. Agent tasks, Agent credentials, public Functions, entities, custom queries, Function execution, and integrations now have native replacements. Microsoft SSO remains available only through the complete deprecated re-export surface.
302
+
303
+ `createClient` configures the legacy SDK with both `baseURL` and `authUrl` set to `${apiUrl}/legacy`, after removing trailing slashes, plus `projectId: appId`. This keeps deprecated calls and legacy login routed through the BFF while the new modules call their native APIs directly. Its `authPageUrl` uses the same precedence as native Google SSO: explicit client config, `window.__mitraEnv.authPageUrl`, then `/sdk-auth.html` on the `apiUrl` origin. Existing query parameters are preserved.
304
+
305
+ The bridge shares the session in both directions. A session persisted under `mitra_auth_{appId}` is handed to the legacy SDK at startup; native sign-in, Google SSO, proactive or reactive refresh, manual token changes, and sign-out update its active configuration. Sessions produced by legacy login or refresh are persisted back under the same new storage key. The bridge only propagates sessions the two SDKs produce: it never starts a login and never triggers a refresh of its own.
306
+
307
+ The legacy package exposes no sign-out API for deleting the refresh token stored in its private `mitra-session` entry. Native sign-out safely removes both credentials from the active legacy configuration, so deprecated calls cannot authenticate or refresh. A later direct call to `configureSdkMitra` can restore that private persisted refresh token; applications should keep configuration ownership in `createClient` during the migration.
308
+
309
+ The legacy SDK does not return a user, so `auth.currentUser` stays empty after a legacy login. Call `mitra.auth.me()` to populate it. Calling `configureSdkMitra` directly replaces the legacy configuration and its refresh hook until the next bridged session change, so it should not be mixed with a client-managed migration.
310
+
144
311
  ## Errors and request behavior
145
312
 
146
313
  API failures throw `MitraApiError`:
@@ -157,9 +324,9 @@ try {
157
324
  }
158
325
  ```
159
326
 
160
- The transport refuses HTTP redirects. Statuses `307` and `308`, opaque redirects, and responses already marked as redirected fail without replay. The only automatic replay is the single request attempted after a successful session refresh on `401`.
327
+ The transport refuses HTTP redirects. Statuses `307` and `308`, opaque redirects, and responses already marked as redirected fail without replay. The only automatic replay is the single request attempted after reactive `401` recovery with either a refreshed token or a session that changed while the original request was in flight.
161
328
 
162
- Before constructing `MitraApiError`, the SDK recursively redacts the token used by the request and credentials in `Bearer` format from the error message, code, details, arrays, values, and object keys.
329
+ Before constructing `MitraApiError`, the SDK recursively redacts the token used by the request and credentials in `Bearer` format from the error message, code, details, arrays, values, and object keys. Values under credential fields such as `accessToken`, `refreshToken`, `apiKey`, `password`, `authorization`, `secret`, and `clientSecret` are also replaced with `[REDACTED]`.
163
330
 
164
331
  ## Development
165
332
 
@@ -168,7 +335,7 @@ npm install
168
335
  npm run check
169
336
  ```
170
337
 
171
- `@mitralab.io/sdk-core@0.1.0` is resolved from the public npm registry and locked by integrity in `package-lock.json`. Do not replace it with a `file:` dependency or a local tarball.
338
+ Platform `1.1.0-beta.0` targets exactly `@mitralab.io/sdk-core@0.2.0-beta.0`. Until that Core prerelease is published, local validation uses its matching tarball through `MITRA_SDK_CORE_TARBALL`. The manifest and lock keep the registry spec and the tarball's verified integrity; root `npm ci` becomes available after Core is published. Do not commit a `file:` dependency.
172
339
 
173
340
  The build produces ESM, CommonJS, `.d.ts`, and `.d.cts` artifacts. Package checks inspect the public tarball with Are The Types Wrong, install it into an isolated consumer, and validate ESM, CommonJS, and TypeScript resolution.
174
341