@introspection-sdk/introspection-browser 0.5.1 → 0.5.3

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.
@@ -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
+ }