@introspection-sdk/introspection-browser 0.5.1 → 0.5.2

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @introspection-sdk/introspection-browser@0.5.1 build /home/runner/work/introspection-js-sdk/introspection-js-sdk/packages/introspection-browser
2
+ > @introspection-sdk/introspection-browser@0.5.2 build /home/runner/work/introspection-js-sdk/introspection-js-sdk/packages/introspection-browser
3
3
  > tsc
4
4
 
package/README.md CHANGED
@@ -30,3 +30,62 @@ client.feedback("thumbs_down", {
30
30
  comments: "Off topic",
31
31
  });
32
32
  ```
33
+
34
+ ## Client-side API (`/api`)
35
+
36
+ The `@introspection-sdk/introspection-browser/api` entry point lets a
37
+ single-page app **create and stream Introspection tasks directly from the
38
+ browser, with no API key in JavaScript**. Authentication is the standard B2B2C
39
+ flow (see the [`sample-auth`](../../examples/apps/sample-auth) example):
40
+
41
+ 1. The app's **own backend ("broker")** mints an Introspection access token —
42
+ via RFC 8693 token-exchange of the partner IdP token, a PKCE
43
+ `authorization_code`, or `client_credentials`. The IdP secret never leaves
44
+ the backend.
45
+ 2. `connect()` redeems that token at the Data Plane
46
+ (`POST /v1/oauth/exchange`) for the HttpOnly `intro_dp_session` cookie.
47
+ 3. Every subsequent call rides that cookie (`credentials: "include"`). When the
48
+ session expires, the client transparently re-mints it once on a `401`.
49
+
50
+ ```typescript
51
+ import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
52
+
53
+ const client = new IntrospectionApiClient({
54
+ dpUrl: "https://dp.us.introspection.dev",
55
+ projectId: "proj_…",
56
+ // your backend returns a fresh Introspection access token
57
+ getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
58
+ });
59
+
60
+ await client.connect(); // → intro_dp_session cookie
61
+
62
+ const run = await client.tasks.start({
63
+ prompt: "Summarize my latest order",
64
+ agent_name: "support-agent", // or runtime_id: "rt_…"
65
+ identity: { user_id: "u_42" }, // folded into metadata.identity
66
+ visibility: "identity", // sharing scope; defaults by credential
67
+ idle_timeout_seconds: 120, // idle window before the sandbox is torn down
68
+ });
69
+
70
+ for await (const ev of run.stream()) {
71
+ console.log(ev.event, ev.data);
72
+ }
73
+ ```
74
+
75
+ `client.tasks` exposes the full CRUD surface (`create` / `start` / `get` /
76
+ `list` / `update` / `delete` / `archive` / `unarchive`) plus per-run streaming
77
+ (`run.stream()`, `run.text()`, `run.cancel()`).
78
+
79
+ Both `create` and `start` accept two optional task controls:
80
+
81
+ - **`visibility`** (`"identity" | "member" | "project"`) — the task's minimum
82
+ sharing scope. Defaults to `"identity"` when the credential carries an
83
+ identity claim, else `"project"`. The owning `identity_key` is always derived
84
+ from the session JWT, never the request body.
85
+ - **`idle_timeout_seconds`** (`number`) — overrides the interactive idle window
86
+ before the sandbox is torn down. `0` tears it down as soon as it's
87
+ provisioned; omit to use the deployment default. Clamped to the task timeout.
88
+
89
+ > **CORS:** the Data Plane authorizes browser origins against its configured
90
+ > allowlist (`CORS_ORIGINS`). A new SPA origin must be present there for the
91
+ > cross-origin cookie exchange and task calls to succeed.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Browser-side Introspection API client.
3
+ *
4
+ * Lets a single-page app create and stream Introspection tasks directly
5
+ * from the browser, with no API key in JavaScript. The auth boundary:
6
+ *
7
+ * 1. The SPA's own backend ("broker") mints an Introspection access
8
+ * token — via RFC 8693 token-exchange of the partner IdP token, a
9
+ * PKCE `authorization_code`, or `client_credentials`. The IdP
10
+ * secret never leaves the backend. The SPA fetches that token
11
+ * through the `getToken` callback.
12
+ * 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
13
+ * for the HttpOnly `intro_dp_session` cookie.
14
+ * 3. Every subsequent call rides that cookie (`credentials: "include"`)
15
+ * — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
16
+ *
17
+ * When the session cookie expires, an in-flight request gets a 401, and
18
+ * the client transparently re-runs `getToken` + the DP exchange once
19
+ * before retrying.
20
+ */
21
+ import { TasksClient } from "./tasks.js";
22
+ export interface IntrospectionApiClientOptions {
23
+ /** Data Plane REST base URL (e.g. `https://dp.us.introspection.dev`). */
24
+ dpUrl: string;
25
+ /** Project the session is scoped to. */
26
+ projectId: string;
27
+ /**
28
+ * Returns a fresh Introspection access token from the app's broker
29
+ * (its own backend). Called on `connect()` and again whenever the DP
30
+ * session cookie needs re-minting after a 401.
31
+ */
32
+ getToken: () => string | Promise<string>;
33
+ /** Custom `fetch` (for tests or non-standard runtimes). */
34
+ fetch?: typeof fetch;
35
+ /** Extra headers merged into every DP request. */
36
+ additionalHeaders?: Record<string, string>;
37
+ }
38
+ export declare class IntrospectionApiClient {
39
+ private readonly opts;
40
+ /** `/v1/tasks` operations bound to the DP session cookie. */
41
+ readonly tasks: TasksClient;
42
+ private readonly fetchImpl;
43
+ constructor(opts: IntrospectionApiClientOptions);
44
+ /**
45
+ * Mint a token via `getToken` and redeem it at the DP for the
46
+ * `intro_dp_session` cookie. Call once before issuing task requests.
47
+ */
48
+ connect(): Promise<void>;
49
+ private exchange;
50
+ /** 401 recovery: re-exchange, reporting success so the call can retry. */
51
+ private reexchange;
52
+ }
53
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,6BAA6B;IAC5C,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,qBAAa,sBAAsB;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAE5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEZ,IAAI,EAAE,6BAA6B;IAgBhE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAIhB,QAAQ;IAiBtB,0EAA0E;YAC5D,UAAU;CAQzB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Browser-side Introspection API client.
3
+ *
4
+ * Lets a single-page app create and stream Introspection tasks directly
5
+ * from the browser, with no API key in JavaScript. The auth boundary:
6
+ *
7
+ * 1. The SPA's own backend ("broker") mints an Introspection access
8
+ * token — via RFC 8693 token-exchange of the partner IdP token, a
9
+ * PKCE `authorization_code`, or `client_credentials`. The IdP
10
+ * secret never leaves the backend. The SPA fetches that token
11
+ * through the `getToken` callback.
12
+ * 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
13
+ * for the HttpOnly `intro_dp_session` cookie.
14
+ * 3. Every subsequent call rides that cookie (`credentials: "include"`)
15
+ * — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
16
+ *
17
+ * When the session cookie expires, an in-flight request gets a 401, and
18
+ * the client transparently re-runs `getToken` + the DP exchange once
19
+ * before retrying.
20
+ */
21
+ import { BrowserHttpClient, stripTrailingSlash, toApiError } from "./http.js";
22
+ import { TasksClient } from "./tasks.js";
23
+ export class IntrospectionApiClient {
24
+ opts;
25
+ /** `/v1/tasks` operations bound to the DP session cookie. */
26
+ tasks;
27
+ fetchImpl;
28
+ constructor(opts) {
29
+ this.opts = opts;
30
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
31
+ if (!this.fetchImpl) {
32
+ throw new Error("global fetch is unavailable; pass `fetch` or run in a modern browser");
33
+ }
34
+ const http = new BrowserHttpClient({
35
+ apiUrl: opts.dpUrl,
36
+ additionalHeaders: opts.additionalHeaders,
37
+ fetch: opts.fetch,
38
+ onUnauthorized: () => this.reexchange(),
39
+ });
40
+ this.tasks = new TasksClient(http);
41
+ }
42
+ /**
43
+ * Mint a token via `getToken` and redeem it at the DP for the
44
+ * `intro_dp_session` cookie. Call once before issuing task requests.
45
+ */
46
+ async connect() {
47
+ await this.exchange();
48
+ }
49
+ async exchange() {
50
+ const token = await this.opts.getToken();
51
+ const res = await this.fetchImpl(`${stripTrailingSlash(this.opts.dpUrl)}/v1/oauth/exchange`, {
52
+ method: "POST",
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ ...(this.opts.additionalHeaders ?? {}),
56
+ },
57
+ body: JSON.stringify({ token, project_id: this.opts.projectId }),
58
+ credentials: "include",
59
+ });
60
+ if (!res.ok)
61
+ throw await toApiError(res);
62
+ }
63
+ /** 401 recovery: re-exchange, reporting success so the call can retry. */
64
+ async reexchange() {
65
+ try {
66
+ await this.exchange();
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ }
74
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAmBzC,MAAM,OAAO,sBAAsB;IAMJ;IAL7B,6DAA6D;IACpD,KAAK,CAAc;IAEX,SAAS,CAAe;IAEzC,YAA6B,IAAmC;QAAnC,SAAI,GAAJ,IAAI,CAA+B;QAC9D,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,iBAAiB,CAAC;YACjC,MAAM,EAAE,IAAI,CAAC,KAAK;YAClB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAC9B,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAC1D;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;aACvC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAChE,WAAW,EAAE,SAAS;SACvB,CACF,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,0EAA0E;IAClE,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Browser HTTP transport for the Introspection Data Plane `/v1` surface.
3
+ *
4
+ * The request/response machinery — URL joining, query building, body
5
+ * serialization, error mapping, SSE — lives in the shared
6
+ * `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
7
+ * browser auth strategy on top of it: the DP is reached with the HttpOnly
8
+ * `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
9
+ * request opts into `credentials: "include"` and sends NO bearer token —
10
+ * no Introspection credential ever lives in JavaScript. A 401 (expired
11
+ * session cookie) triggers a single `onUnauthorized` refresh + retry.
12
+ */
13
+ import { BaseHttpClient, stripTrailingSlash, toApiError } from "@introspection-sdk/http";
14
+ export { stripTrailingSlash, toApiError };
15
+ export interface BrowserHttpConfig {
16
+ /** DP REST base URL every request is prefixed with. */
17
+ apiUrl: string;
18
+ /** Extra headers merged into every request. */
19
+ additionalHeaders?: Record<string, string>;
20
+ /** Custom `fetch` (for tests or non-standard runtimes). */
21
+ fetch?: typeof fetch;
22
+ /**
23
+ * Invoked when a request comes back `401`. Return `true` if the DP
24
+ * session was refreshed and the request should be retried once;
25
+ * `false` to surface the original error.
26
+ */
27
+ onUnauthorized?: () => Promise<boolean>;
28
+ }
29
+ /**
30
+ * Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
31
+ * opinion about which resource it serves — the caller picks the path.
32
+ */
33
+ export declare class BrowserHttpClient extends BaseHttpClient {
34
+ constructor(cfg: BrowserHttpConfig);
35
+ }
36
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,EACX,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;gBACvC,GAAG,EAAE,iBAAiB;CAYnC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Browser HTTP transport for the Introspection Data Plane `/v1` surface.
3
+ *
4
+ * The request/response machinery — URL joining, query building, body
5
+ * serialization, error mapping, SSE — lives in the shared
6
+ * `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
7
+ * browser auth strategy on top of it: the DP is reached with the HttpOnly
8
+ * `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
9
+ * request opts into `credentials: "include"` and sends NO bearer token —
10
+ * no Introspection credential ever lives in JavaScript. A 401 (expired
11
+ * session cookie) triggers a single `onUnauthorized` refresh + retry.
12
+ */
13
+ import { BaseHttpClient, stripTrailingSlash, toApiError, } from "@introspection-sdk/http";
14
+ // Re-exported for `client.ts`, which redeems the DP session on `connect()`.
15
+ export { stripTrailingSlash, toApiError };
16
+ /**
17
+ * Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
18
+ * opinion about which resource it serves — the caller picks the path.
19
+ */
20
+ export class BrowserHttpClient extends BaseHttpClient {
21
+ constructor(cfg) {
22
+ super({
23
+ apiUrl: cfg.apiUrl,
24
+ additionalHeaders: cfg.additionalHeaders,
25
+ fetch: cfg.fetch,
26
+ transport: {
27
+ authHeaders: () => ({}),
28
+ credentials: "include",
29
+ onUnauthorized: cfg.onUnauthorized,
30
+ },
31
+ });
32
+ }
33
+ }
34
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,4EAA4E;AAC5E,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC;AAiB1C;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,cAAc;IACnD,YAAY,GAAsB;QAChC,KAAK,CAAC;YACJ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;YACxC,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,SAAS,EAAE;gBACT,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;gBACvB,WAAW,EAAE,SAAS;gBACtB,cAAc,EAAE,GAAG,CAAC,cAAc;aACnC;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
3
+ *
4
+ * A cookie-authenticated client for creating and streaming Introspection
5
+ * tasks directly from a single-page app, with no API key in the browser.
6
+ * Separate from the package's default telemetry export so apps only pull
7
+ * in what they use.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
12
+ *
13
+ * const client = new IntrospectionApiClient({
14
+ * dpUrl: "https://dp.us.introspection.dev",
15
+ * projectId: "proj_…",
16
+ * // your backend mints the Introspection access token
17
+ * getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
18
+ * });
19
+ *
20
+ * await client.connect(); // → intro_dp_session cookie
21
+ * const run = await client.tasks.start({
22
+ * prompt: "Summarize my latest order",
23
+ * agent_name: "support-agent",
24
+ * identity: { user_id: "u_42" },
25
+ * });
26
+ * for await (const ev of run.stream()) console.log(ev.event, ev.data);
27
+ * ```
28
+ */
29
+ export { IntrospectionApiClient, type IntrospectionApiClientOptions, } from "./client.js";
30
+ export { TasksClient, TaskRunsClient, RunHandle, type CreateTaskParams, type StartTaskParams, } from "./tasks.js";
31
+ export { BrowserHttpClient, type BrowserHttpConfig } from "./http.js";
32
+ export { parseSse } from "./sse.js";
33
+ export { Paginator, cursorPaginate, type PageSource, } from "@introspection-sdk/http";
34
+ export type { Task, TaskRun, TaskStatus, TaskVisibility, TaskCreateResponse, TaskListParams, TaskUpdateParams, TaskRunCreateParams, TaskCancelResponse, RunIdentityInput, Paginated, SseEvent, } from "@introspection-sdk/types";
35
+ export { IntrospectionAPIError, AuthenticationError, InsufficientScopeError, NotFoundError, NetworkError, } from "@introspection-sdk/types";
36
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EACL,SAAS,EACT,cAAc,EACd,KAAK,UAAU,GAChB,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EACV,IAAI,EACJ,OAAO,EACP,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,YAAY,GACb,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
3
+ *
4
+ * A cookie-authenticated client for creating and streaming Introspection
5
+ * tasks directly from a single-page app, with no API key in the browser.
6
+ * Separate from the package's default telemetry export so apps only pull
7
+ * in what they use.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
12
+ *
13
+ * const client = new IntrospectionApiClient({
14
+ * dpUrl: "https://dp.us.introspection.dev",
15
+ * projectId: "proj_…",
16
+ * // your backend mints the Introspection access token
17
+ * getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
18
+ * });
19
+ *
20
+ * await client.connect(); // → intro_dp_session cookie
21
+ * const run = await client.tasks.start({
22
+ * prompt: "Summarize my latest order",
23
+ * agent_name: "support-agent",
24
+ * identity: { user_id: "u_42" },
25
+ * });
26
+ * for await (const ev of run.stream()) console.log(ev.event, ev.data);
27
+ * ```
28
+ */
29
+ export { IntrospectionApiClient, } from "./client.js";
30
+ export { TasksClient, TaskRunsClient, RunHandle, } from "./tasks.js";
31
+ export { BrowserHttpClient } from "./http.js";
32
+ export { parseSse } from "./sse.js";
33
+ export { Paginator, cursorPaginate, } from "@introspection-sdk/http";
34
+ export { IntrospectionAPIError, AuthenticationError, InsufficientScopeError, NotFoundError, NetworkError, } from "@introspection-sdk/types";
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EACL,sBAAsB,GAEvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,GAGV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAA0B,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EACL,SAAS,EACT,cAAc,GAEf,MAAM,yBAAyB,CAAC;AAiBjC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,YAAY,GACb,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * SSE parsing now lives in the shared `@introspection-sdk/http` core so
3
+ * the browser and Node SDKs stay byte-for-byte identical. Re-exported
4
+ * here to keep the existing `./sse.js` import path stable.
5
+ */
6
+ export { parseSse } from "@introspection-sdk/http";
7
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/api/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * SSE parsing now lives in the shared `@introspection-sdk/http` core so
3
+ * the browser and Node SDKs stay byte-for-byte identical. Re-exported
4
+ * here to keep the existing `./sse.js` import path stable.
5
+ */
6
+ export { parseSse } from "@introspection-sdk/http";
7
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/api/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { RunIdentityInput, SseEvent, Task, TaskCancelResponse, TaskCreateResponse, TaskListParams, TaskRun, TaskRunCreateParams, TaskUpdateParams, TaskVisibility } from "@introspection-sdk/types";
2
+ import { Paginator } from "@introspection-sdk/http";
3
+ import { BrowserHttpClient } from "./http.js";
4
+ /**
5
+ * Body for creating a task directly against the DP from the browser.
6
+ *
7
+ * A browser caller has no pre-pinned `Runner`, so — unlike the Node
8
+ * SDK's runner-bound create — it selects the agent itself: pass
9
+ * `runtime_id` to pin a recipe runtime, or `agent_name` to fall back to
10
+ * a named recipe agent. `identity` is a convenience that is folded into
11
+ * `metadata.identity` for attribution (the DP derives the owning
12
+ * `identity_key` from the session's JWT claims, never from this body).
13
+ */
14
+ export interface CreateTaskParams {
15
+ prompt?: string;
16
+ /** Pin the task to a specific recipe runtime. */
17
+ runtime_id?: string;
18
+ /** Named recipe agent, when no `runtime_id` is pinned. */
19
+ agent_name?: string;
20
+ title?: string;
21
+ metadata?: Record<string, unknown>;
22
+ visibility?: TaskVisibility;
23
+ /**
24
+ * Override the interactive idle window (seconds) before the sandbox is
25
+ * torn down. `0` tears down as soon as it's provisioned; omit to use the
26
+ * deployment default. Clamped to the task timeout.
27
+ */
28
+ idle_timeout_seconds?: number;
29
+ /** Caller identity for attribution; merged into `metadata.identity`. */
30
+ identity?: RunIdentityInput;
31
+ }
32
+ export interface StartTaskParams extends CreateTaskParams {
33
+ /** Required for the `start()` sugar that returns a run handle. */
34
+ prompt: string;
35
+ }
36
+ /** Live handle on a task's run: stream its events, collect text, cancel. */
37
+ export declare class RunHandle {
38
+ readonly task: Task | null;
39
+ readonly run: TaskRun;
40
+ private readonly runs;
41
+ constructor(task: Task | null, run: TaskRun, runs: TaskRunsClient);
42
+ stream(): AsyncIterable<SseEvent>;
43
+ cancel(): Promise<TaskCancelResponse>;
44
+ /** Convenience: concatenate `data` from `text`/`message` SSE frames. */
45
+ text(): Promise<string>;
46
+ }
47
+ export declare class TaskRunsClient {
48
+ private readonly http;
49
+ constructor(http: BrowserHttpClient);
50
+ create(taskId: string, body: TaskRunCreateParams): Promise<RunHandle>;
51
+ get(taskId: string, runId: string): Promise<TaskRun>;
52
+ cancel(taskId: string, runId: string): Promise<TaskCancelResponse>;
53
+ stream(taskId: string, runId: string): AsyncIterable<SseEvent>;
54
+ }
55
+ /**
56
+ * Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
57
+ * `TasksApi` shape but is bound to a DP session cookie rather than a
58
+ * bearer token, and lets the caller select the agent on create.
59
+ */
60
+ export declare class TasksClient {
61
+ private readonly http;
62
+ readonly runs: TaskRunsClient;
63
+ constructor(http: BrowserHttpClient);
64
+ /**
65
+ * Tasks matching `params`. `await` it for the first page (preserving
66
+ * the wire envelope's counts + `next` cursor), or `for await` it to
67
+ * stream every task across pages (fetched lazily — `limit` sets the
68
+ * page size, `next` the starting cursor; stop early to stop fetching).
69
+ *
70
+ * Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
71
+ * for the `/v1/tasks` shape.
72
+ */
73
+ list(params?: TaskListParams): Paginator<Task>;
74
+ create(body: CreateTaskParams): Promise<TaskCreateResponse>;
75
+ get(taskId: string): Promise<Task>;
76
+ update(taskId: string, body: TaskUpdateParams): Promise<Task>;
77
+ delete(taskId: string): Promise<void>;
78
+ archive(taskId: string): Promise<void>;
79
+ unarchive(taskId: string): Promise<void>;
80
+ /** Sugar: create a task + return a handle on its initial run. */
81
+ start(params: StartTaskParams): Promise<RunHandle>;
82
+ }
83
+ //# sourceMappingURL=tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/api/tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import { cursorPaginate } from "@introspection-sdk/http";
2
+ import { parseSse } from "./sse.js";
3
+ function toTaskBody(params) {
4
+ const { identity, metadata, ...rest } = params;
5
+ const body = { ...rest };
6
+ const mergedMetadata = identity
7
+ ? { ...(metadata ?? {}), identity }
8
+ : metadata;
9
+ if (mergedMetadata !== undefined)
10
+ body.metadata = mergedMetadata;
11
+ return body;
12
+ }
13
+ /** Live handle on a task's run: stream its events, collect text, cancel. */
14
+ export class RunHandle {
15
+ task;
16
+ run;
17
+ runs;
18
+ constructor(task, run, runs) {
19
+ this.task = task;
20
+ this.run = run;
21
+ this.runs = runs;
22
+ }
23
+ stream() {
24
+ return this.runs.stream(this.run.task_id, this.run.id);
25
+ }
26
+ cancel() {
27
+ return this.runs.cancel(this.run.task_id, this.run.id);
28
+ }
29
+ /** Convenience: concatenate `data` from `text`/`message` SSE frames. */
30
+ async text() {
31
+ let out = "";
32
+ for await (const ev of this.stream()) {
33
+ if (ev.event === "text" || ev.event === "message")
34
+ out += ev.data;
35
+ }
36
+ return out;
37
+ }
38
+ }
39
+ export class TaskRunsClient {
40
+ http;
41
+ constructor(http) {
42
+ this.http = http;
43
+ }
44
+ async create(taskId, body) {
45
+ const res = await this.http.request({
46
+ method: "POST",
47
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs`,
48
+ body,
49
+ });
50
+ return new RunHandle(null, res.run, this);
51
+ }
52
+ get(taskId, runId) {
53
+ return this.http.request({
54
+ method: "GET",
55
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}`,
56
+ });
57
+ }
58
+ cancel(taskId, runId) {
59
+ return this.http.request({
60
+ method: "POST",
61
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/cancel`,
62
+ });
63
+ }
64
+ async *stream(taskId, runId) {
65
+ const res = await this.http.stream({
66
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/stream`,
67
+ });
68
+ yield* parseSse(res);
69
+ }
70
+ }
71
+ /**
72
+ * Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
73
+ * `TasksApi` shape but is bound to a DP session cookie rather than a
74
+ * bearer token, and lets the caller select the agent on create.
75
+ */
76
+ export class TasksClient {
77
+ http;
78
+ runs;
79
+ constructor(http) {
80
+ this.http = http;
81
+ this.runs = new TaskRunsClient(http);
82
+ }
83
+ /**
84
+ * Tasks matching `params`. `await` it for the first page (preserving
85
+ * the wire envelope's counts + `next` cursor), or `for await` it to
86
+ * stream every task across pages (fetched lazily — `limit` sets the
87
+ * page size, `next` the starting cursor; stop early to stop fetching).
88
+ *
89
+ * Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
90
+ * for the `/v1/tasks` shape.
91
+ */
92
+ list(params) {
93
+ return cursorPaginate((next) => this.http.request({
94
+ method: "GET",
95
+ path: "/v1/tasks",
96
+ query: { ...params, next },
97
+ }), params?.next);
98
+ }
99
+ create(body) {
100
+ return this.http.request({
101
+ method: "POST",
102
+ path: "/v1/tasks",
103
+ body: toTaskBody(body),
104
+ });
105
+ }
106
+ get(taskId) {
107
+ return this.http.request({
108
+ method: "GET",
109
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
110
+ });
111
+ }
112
+ update(taskId, body) {
113
+ return this.http.request({
114
+ method: "PATCH",
115
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
116
+ body,
117
+ });
118
+ }
119
+ delete(taskId) {
120
+ return this.http.request({
121
+ method: "DELETE",
122
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
123
+ expect: "empty",
124
+ });
125
+ }
126
+ archive(taskId) {
127
+ return this.http.request({
128
+ method: "POST",
129
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/archive`,
130
+ expect: "empty",
131
+ });
132
+ }
133
+ unarchive(taskId) {
134
+ return this.http.request({
135
+ method: "POST",
136
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/unarchive`,
137
+ expect: "empty",
138
+ });
139
+ }
140
+ /** Sugar: create a task + return a handle on its initial run. */
141
+ async start(params) {
142
+ const res = await this.create(params);
143
+ return new RunHandle(res.task, res.run, this.runs);
144
+ }
145
+ }
146
+ //# sourceMappingURL=tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/api/tasks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@introspection-sdk/introspection-browser",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Introspection observability SDK for the browser",
6
6
  "engines": {
@@ -13,6 +13,10 @@
13
13
  ".": {
14
14
  "types": "./dist/index.d.ts",
15
15
  "default": "./dist/index.js"
16
+ },
17
+ "./api": {
18
+ "types": "./dist/api/index.d.ts",
19
+ "default": "./dist/api/index.js"
16
20
  }
17
21
  },
18
22
  "devDependencies": {
@@ -24,10 +28,11 @@
24
28
  },
25
29
  "dependencies": {
26
30
  "@opentelemetry/api": "^1.9.1",
27
- "@opentelemetry/api-logs": "^0.218.0",
28
- "@opentelemetry/sdk-logs": "^0.218.0",
29
- "@opentelemetry/exporter-logs-otlp-http": "^0.218.0",
30
- "@introspection-sdk/types": "0.5.1"
31
+ "@opentelemetry/api-logs": "^0.219.0",
32
+ "@opentelemetry/sdk-logs": "^0.219.0",
33
+ "@opentelemetry/exporter-logs-otlp-http": "^0.219.0",
34
+ "@introspection-sdk/http": "0.5.2",
35
+ "@introspection-sdk/types": "0.5.2"
31
36
  },
32
37
  "scripts": {
33
38
  "lint": "eslint . --max-warnings 0",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Browser-side Introspection API client.
3
+ *
4
+ * Lets a single-page app create and stream Introspection tasks directly
5
+ * from the browser, with no API key in JavaScript. The auth boundary:
6
+ *
7
+ * 1. The SPA's own backend ("broker") mints an Introspection access
8
+ * token — via RFC 8693 token-exchange of the partner IdP token, a
9
+ * PKCE `authorization_code`, or `client_credentials`. The IdP
10
+ * secret never leaves the backend. The SPA fetches that token
11
+ * through the `getToken` callback.
12
+ * 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
13
+ * for the HttpOnly `intro_dp_session` cookie.
14
+ * 3. Every subsequent call rides that cookie (`credentials: "include"`)
15
+ * — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
16
+ *
17
+ * When the session cookie expires, an in-flight request gets a 401, and
18
+ * the client transparently re-runs `getToken` + the DP exchange once
19
+ * before retrying.
20
+ */
21
+
22
+ import { BrowserHttpClient, stripTrailingSlash, toApiError } from "./http.js";
23
+ import { TasksClient } from "./tasks.js";
24
+
25
+ export interface IntrospectionApiClientOptions {
26
+ /** Data Plane REST base URL (e.g. `https://dp.us.introspection.dev`). */
27
+ dpUrl: string;
28
+ /** Project the session is scoped to. */
29
+ projectId: string;
30
+ /**
31
+ * Returns a fresh Introspection access token from the app's broker
32
+ * (its own backend). Called on `connect()` and again whenever the DP
33
+ * session cookie needs re-minting after a 401.
34
+ */
35
+ getToken: () => string | Promise<string>;
36
+ /** Custom `fetch` (for tests or non-standard runtimes). */
37
+ fetch?: typeof fetch;
38
+ /** Extra headers merged into every DP request. */
39
+ additionalHeaders?: Record<string, string>;
40
+ }
41
+
42
+ export class IntrospectionApiClient {
43
+ /** `/v1/tasks` operations bound to the DP session cookie. */
44
+ readonly tasks: TasksClient;
45
+
46
+ private readonly fetchImpl: typeof fetch;
47
+
48
+ constructor(private readonly opts: IntrospectionApiClientOptions) {
49
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
50
+ if (!this.fetchImpl) {
51
+ throw new Error(
52
+ "global fetch is unavailable; pass `fetch` or run in a modern browser",
53
+ );
54
+ }
55
+ const http = new BrowserHttpClient({
56
+ apiUrl: opts.dpUrl,
57
+ additionalHeaders: opts.additionalHeaders,
58
+ fetch: opts.fetch,
59
+ onUnauthorized: () => this.reexchange(),
60
+ });
61
+ this.tasks = new TasksClient(http);
62
+ }
63
+
64
+ /**
65
+ * Mint a token via `getToken` and redeem it at the DP for the
66
+ * `intro_dp_session` cookie. Call once before issuing task requests.
67
+ */
68
+ async connect(): Promise<void> {
69
+ await this.exchange();
70
+ }
71
+
72
+ private async exchange(): Promise<void> {
73
+ const token = await this.opts.getToken();
74
+ const res = await this.fetchImpl(
75
+ `${stripTrailingSlash(this.opts.dpUrl)}/v1/oauth/exchange`,
76
+ {
77
+ method: "POST",
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ ...(this.opts.additionalHeaders ?? {}),
81
+ },
82
+ body: JSON.stringify({ token, project_id: this.opts.projectId }),
83
+ credentials: "include",
84
+ },
85
+ );
86
+ if (!res.ok) throw await toApiError(res);
87
+ }
88
+
89
+ /** 401 recovery: re-exchange, reporting success so the call can retry. */
90
+ private async reexchange(): Promise<boolean> {
91
+ try {
92
+ await this.exchange();
93
+ return true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Browser HTTP transport for the Introspection Data Plane `/v1` surface.
3
+ *
4
+ * The request/response machinery — URL joining, query building, body
5
+ * serialization, error mapping, SSE — lives in the shared
6
+ * `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
7
+ * browser auth strategy on top of it: the DP is reached with the HttpOnly
8
+ * `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
9
+ * request opts into `credentials: "include"` and sends NO bearer token —
10
+ * no Introspection credential ever lives in JavaScript. A 401 (expired
11
+ * session cookie) triggers a single `onUnauthorized` refresh + retry.
12
+ */
13
+
14
+ import {
15
+ BaseHttpClient,
16
+ stripTrailingSlash,
17
+ toApiError,
18
+ } from "@introspection-sdk/http";
19
+
20
+ // Re-exported for `client.ts`, which redeems the DP session on `connect()`.
21
+ export { stripTrailingSlash, toApiError };
22
+
23
+ export interface BrowserHttpConfig {
24
+ /** DP REST base URL every request is prefixed with. */
25
+ apiUrl: string;
26
+ /** Extra headers merged into every request. */
27
+ additionalHeaders?: Record<string, string>;
28
+ /** Custom `fetch` (for tests or non-standard runtimes). */
29
+ fetch?: typeof fetch;
30
+ /**
31
+ * Invoked when a request comes back `401`. Return `true` if the DP
32
+ * session was refreshed and the request should be retried once;
33
+ * `false` to surface the original error.
34
+ */
35
+ onUnauthorized?: () => Promise<boolean>;
36
+ }
37
+
38
+ /**
39
+ * Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
40
+ * opinion about which resource it serves — the caller picks the path.
41
+ */
42
+ export class BrowserHttpClient extends BaseHttpClient {
43
+ constructor(cfg: BrowserHttpConfig) {
44
+ super({
45
+ apiUrl: cfg.apiUrl,
46
+ additionalHeaders: cfg.additionalHeaders,
47
+ fetch: cfg.fetch,
48
+ transport: {
49
+ authHeaders: () => ({}),
50
+ credentials: "include",
51
+ onUnauthorized: cfg.onUnauthorized,
52
+ },
53
+ });
54
+ }
55
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
3
+ *
4
+ * A cookie-authenticated client for creating and streaming Introspection
5
+ * tasks directly from a single-page app, with no API key in the browser.
6
+ * Separate from the package's default telemetry export so apps only pull
7
+ * in what they use.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
12
+ *
13
+ * const client = new IntrospectionApiClient({
14
+ * dpUrl: "https://dp.us.introspection.dev",
15
+ * projectId: "proj_…",
16
+ * // your backend mints the Introspection access token
17
+ * getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
18
+ * });
19
+ *
20
+ * await client.connect(); // → intro_dp_session cookie
21
+ * const run = await client.tasks.start({
22
+ * prompt: "Summarize my latest order",
23
+ * agent_name: "support-agent",
24
+ * identity: { user_id: "u_42" },
25
+ * });
26
+ * for await (const ev of run.stream()) console.log(ev.event, ev.data);
27
+ * ```
28
+ */
29
+
30
+ export {
31
+ IntrospectionApiClient,
32
+ type IntrospectionApiClientOptions,
33
+ } from "./client.js";
34
+ export {
35
+ TasksClient,
36
+ TaskRunsClient,
37
+ RunHandle,
38
+ type CreateTaskParams,
39
+ type StartTaskParams,
40
+ } from "./tasks.js";
41
+ export { BrowserHttpClient, type BrowserHttpConfig } from "./http.js";
42
+ export { parseSse } from "./sse.js";
43
+ export {
44
+ Paginator,
45
+ cursorPaginate,
46
+ type PageSource,
47
+ } from "@introspection-sdk/http";
48
+
49
+ // Re-exported wire types for convenience.
50
+ export type {
51
+ Task,
52
+ TaskRun,
53
+ TaskStatus,
54
+ TaskVisibility,
55
+ TaskCreateResponse,
56
+ TaskListParams,
57
+ TaskUpdateParams,
58
+ TaskRunCreateParams,
59
+ TaskCancelResponse,
60
+ RunIdentityInput,
61
+ Paginated,
62
+ SseEvent,
63
+ } from "@introspection-sdk/types";
64
+ export {
65
+ IntrospectionAPIError,
66
+ AuthenticationError,
67
+ InsufficientScopeError,
68
+ NotFoundError,
69
+ NetworkError,
70
+ } from "@introspection-sdk/types";
package/src/api/sse.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * SSE parsing now lives in the shared `@introspection-sdk/http` core so
3
+ * the browser and Node SDKs stay byte-for-byte identical. Re-exported
4
+ * here to keep the existing `./sse.js` import path stable.
5
+ */
6
+ export { parseSse } from "@introspection-sdk/http";
@@ -0,0 +1,208 @@
1
+ import type {
2
+ Paginated,
3
+ RunIdentityInput,
4
+ SseEvent,
5
+ Task,
6
+ TaskCancelResponse,
7
+ TaskCreateResponse,
8
+ TaskListParams,
9
+ TaskRun,
10
+ TaskRunCreateParams,
11
+ TaskRunResponse,
12
+ TaskUpdateParams,
13
+ TaskVisibility,
14
+ } from "@introspection-sdk/types";
15
+ import { Paginator, cursorPaginate } from "@introspection-sdk/http";
16
+ import { BrowserHttpClient } from "./http.js";
17
+ import { parseSse } from "./sse.js";
18
+
19
+ /**
20
+ * Body for creating a task directly against the DP from the browser.
21
+ *
22
+ * A browser caller has no pre-pinned `Runner`, so — unlike the Node
23
+ * SDK's runner-bound create — it selects the agent itself: pass
24
+ * `runtime_id` to pin a recipe runtime, or `agent_name` to fall back to
25
+ * a named recipe agent. `identity` is a convenience that is folded into
26
+ * `metadata.identity` for attribution (the DP derives the owning
27
+ * `identity_key` from the session's JWT claims, never from this body).
28
+ */
29
+ export interface CreateTaskParams {
30
+ prompt?: string;
31
+ /** Pin the task to a specific recipe runtime. */
32
+ runtime_id?: string;
33
+ /** Named recipe agent, when no `runtime_id` is pinned. */
34
+ agent_name?: string;
35
+ title?: string;
36
+ metadata?: Record<string, unknown>;
37
+ visibility?: TaskVisibility;
38
+ /**
39
+ * Override the interactive idle window (seconds) before the sandbox is
40
+ * torn down. `0` tears down as soon as it's provisioned; omit to use the
41
+ * deployment default. Clamped to the task timeout.
42
+ */
43
+ idle_timeout_seconds?: number;
44
+ /** Caller identity for attribution; merged into `metadata.identity`. */
45
+ identity?: RunIdentityInput;
46
+ }
47
+
48
+ export interface StartTaskParams extends CreateTaskParams {
49
+ /** Required for the `start()` sugar that returns a run handle. */
50
+ prompt: string;
51
+ }
52
+
53
+ function toTaskBody(params: CreateTaskParams): Record<string, unknown> {
54
+ const { identity, metadata, ...rest } = params;
55
+ const body: Record<string, unknown> = { ...rest };
56
+ const mergedMetadata = identity
57
+ ? { ...(metadata ?? {}), identity }
58
+ : metadata;
59
+ if (mergedMetadata !== undefined) body.metadata = mergedMetadata;
60
+ return body;
61
+ }
62
+
63
+ /** Live handle on a task's run: stream its events, collect text, cancel. */
64
+ export class RunHandle {
65
+ constructor(
66
+ public readonly task: Task | null,
67
+ public readonly run: TaskRun,
68
+ private readonly runs: TaskRunsClient,
69
+ ) {}
70
+
71
+ stream(): AsyncIterable<SseEvent> {
72
+ return this.runs.stream(this.run.task_id, this.run.id);
73
+ }
74
+
75
+ cancel(): Promise<TaskCancelResponse> {
76
+ return this.runs.cancel(this.run.task_id, this.run.id);
77
+ }
78
+
79
+ /** Convenience: concatenate `data` from `text`/`message` SSE frames. */
80
+ async text(): Promise<string> {
81
+ let out = "";
82
+ for await (const ev of this.stream()) {
83
+ if (ev.event === "text" || ev.event === "message") out += ev.data;
84
+ }
85
+ return out;
86
+ }
87
+ }
88
+
89
+ export class TaskRunsClient {
90
+ constructor(private readonly http: BrowserHttpClient) {}
91
+
92
+ async create(taskId: string, body: TaskRunCreateParams): Promise<RunHandle> {
93
+ const res = await this.http.request<TaskRunResponse>({
94
+ method: "POST",
95
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs`,
96
+ body,
97
+ });
98
+ return new RunHandle(null, res.run, this);
99
+ }
100
+
101
+ get(taskId: string, runId: string): Promise<TaskRun> {
102
+ return this.http.request<TaskRun>({
103
+ method: "GET",
104
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}`,
105
+ });
106
+ }
107
+
108
+ cancel(taskId: string, runId: string): Promise<TaskCancelResponse> {
109
+ return this.http.request<TaskCancelResponse>({
110
+ method: "POST",
111
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/cancel`,
112
+ });
113
+ }
114
+
115
+ async *stream(taskId: string, runId: string): AsyncIterable<SseEvent> {
116
+ const res = await this.http.stream({
117
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/stream`,
118
+ });
119
+ yield* parseSse(res);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
125
+ * `TasksApi` shape but is bound to a DP session cookie rather than a
126
+ * bearer token, and lets the caller select the agent on create.
127
+ */
128
+ export class TasksClient {
129
+ readonly runs: TaskRunsClient;
130
+
131
+ constructor(private readonly http: BrowserHttpClient) {
132
+ this.runs = new TaskRunsClient(http);
133
+ }
134
+
135
+ /**
136
+ * Tasks matching `params`. `await` it for the first page (preserving
137
+ * the wire envelope's counts + `next` cursor), or `for await` it to
138
+ * stream every task across pages (fetched lazily — `limit` sets the
139
+ * page size, `next` the starting cursor; stop early to stop fetching).
140
+ *
141
+ * Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
142
+ * for the `/v1/tasks` shape.
143
+ */
144
+ list(params?: TaskListParams): Paginator<Task> {
145
+ return cursorPaginate(
146
+ (next) =>
147
+ this.http.request<Paginated<Task>>({
148
+ method: "GET",
149
+ path: "/v1/tasks",
150
+ query: { ...params, next } as Record<string, unknown>,
151
+ }),
152
+ params?.next,
153
+ );
154
+ }
155
+
156
+ create(body: CreateTaskParams): Promise<TaskCreateResponse> {
157
+ return this.http.request<TaskCreateResponse>({
158
+ method: "POST",
159
+ path: "/v1/tasks",
160
+ body: toTaskBody(body),
161
+ });
162
+ }
163
+
164
+ get(taskId: string): Promise<Task> {
165
+ return this.http.request<Task>({
166
+ method: "GET",
167
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
168
+ });
169
+ }
170
+
171
+ update(taskId: string, body: TaskUpdateParams): Promise<Task> {
172
+ return this.http.request<Task>({
173
+ method: "PATCH",
174
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
175
+ body,
176
+ });
177
+ }
178
+
179
+ delete(taskId: string): Promise<void> {
180
+ return this.http.request<void>({
181
+ method: "DELETE",
182
+ path: `/v1/tasks/${encodeURIComponent(taskId)}`,
183
+ expect: "empty",
184
+ });
185
+ }
186
+
187
+ archive(taskId: string): Promise<void> {
188
+ return this.http.request<void>({
189
+ method: "POST",
190
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/archive`,
191
+ expect: "empty",
192
+ });
193
+ }
194
+
195
+ unarchive(taskId: string): Promise<void> {
196
+ return this.http.request<void>({
197
+ method: "POST",
198
+ path: `/v1/tasks/${encodeURIComponent(taskId)}/unarchive`,
199
+ expect: "empty",
200
+ });
201
+ }
202
+
203
+ /** Sugar: create a task + return a handle on its initial run. */
204
+ async start(params: StartTaskParams): Promise<RunHandle> {
205
+ const res = await this.create(params);
206
+ return new RunHandle(res.task, res.run, this.runs);
207
+ }
208
+ }