@lightspeed-ai/agent-client 0.1.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.
Files changed (47) hide show
  1. package/README.md +63 -0
  2. package/dist/client.d.ts +65 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +194 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/errors.d.ts +20 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +47 -0
  9. package/dist/errors.js.map +1 -0
  10. package/dist/generated/methods.d.ts +2497 -0
  11. package/dist/generated/methods.d.ts.map +1 -0
  12. package/dist/generated/methods.js +1751 -0
  13. package/dist/generated/methods.js.map +1 -0
  14. package/dist/generated/types.d.ts +6950 -0
  15. package/dist/generated/types.d.ts.map +1 -0
  16. package/dist/generated/types.js +6 -0
  17. package/dist/generated/types.js.map +1 -0
  18. package/dist/generated/workflow-manifest.d.ts +219 -0
  19. package/dist/generated/workflow-manifest.d.ts.map +1 -0
  20. package/dist/generated/workflow-manifest.js +251 -0
  21. package/dist/generated/workflow-manifest.js.map +1 -0
  22. package/dist/generated/workflow-types.d.ts +451 -0
  23. package/dist/generated/workflow-types.d.ts.map +1 -0
  24. package/dist/generated/workflow-types.js +6 -0
  25. package/dist/generated/workflow-types.js.map +1 -0
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/workflow.d.ts +217 -0
  31. package/dist/workflow.d.ts.map +1 -0
  32. package/dist/workflow.js +338 -0
  33. package/dist/workflow.js.map +1 -0
  34. package/examples/temporal-activity.ts +33 -0
  35. package/package.json +55 -0
  36. package/release.json +4 -0
  37. package/schema/api.schema.json +16643 -0
  38. package/schema/workflow.json +246 -0
  39. package/schema/workflow.schema.json +880 -0
  40. package/src/client.ts +318 -0
  41. package/src/errors.ts +64 -0
  42. package/src/generated/methods.ts +2888 -0
  43. package/src/generated/types.ts +7189 -0
  44. package/src/generated/workflow-manifest.ts +251 -0
  45. package/src/generated/workflow-types.ts +468 -0
  46. package/src/index.ts +31 -0
  47. package/src/workflow.ts +468 -0
package/src/client.ts ADDED
@@ -0,0 +1,318 @@
1
+ import type {
2
+ AgentApiOutcomeOfRunStartResponse,
3
+ AgentApiOutcomeOfSessionEventsReadResponse,
4
+ EventCursor,
5
+ InputItem,
6
+ RunStartConfig,
7
+ RunStartParams,
8
+ RunStartSource,
9
+ SessionEventView,
10
+ SessionEventsReadParams,
11
+ } from "./generated/types.js";
12
+ import type {
13
+ Method,
14
+ MethodParams,
15
+ MethodResult,
16
+ RpcCaller,
17
+ } from "./generated/methods.js";
18
+ import { LightspeedRpcError, LightspeedTransportError, type JsonRpcErrorPayload } from "./errors.js";
19
+
20
+ export type RequestId = number | string;
21
+
22
+ export interface CallOptions {
23
+ headers?: HeadersInit;
24
+ signal?: AbortSignal;
25
+ }
26
+
27
+ export interface LightspeedClientOptions {
28
+ endpoint: string | URL;
29
+ fetch?: typeof fetch;
30
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
31
+ requestId?: () => RequestId;
32
+ }
33
+
34
+ interface JsonRpcResponse {
35
+ id?: RequestId;
36
+ result?: unknown;
37
+ error?: JsonRpcErrorPayload;
38
+ }
39
+
40
+ export interface StartRunOptions extends CallOptions {
41
+ config?: RunStartConfig | null;
42
+ submissionId?: string | null;
43
+ }
44
+
45
+ export interface ReadEventsOptions extends CallOptions {
46
+ after?: EventCursor | null;
47
+ limit?: number | null;
48
+ waitMs?: number | null;
49
+ }
50
+
51
+ export type RunTerminalState =
52
+ | {
53
+ status: "completed";
54
+ event: SessionEventView;
55
+ outputRef: string | null;
56
+ }
57
+ | {
58
+ status: "failed";
59
+ event: SessionEventView;
60
+ message: string;
61
+ }
62
+ | {
63
+ status: "cancelled";
64
+ event: SessionEventView;
65
+ };
66
+
67
+ export interface AwaitRunOptions extends CallOptions {
68
+ after?: EventCursor | null;
69
+ limit?: number | null;
70
+ waitMs?: number | null;
71
+ heartbeat?: (
72
+ cursor: EventCursor | null,
73
+ page: AgentApiOutcomeOfSessionEventsReadResponse,
74
+ ) => void | Promise<void>;
75
+ onEvent?: (event: SessionEventView) => void | Promise<void>;
76
+ onPage?: (page: AgentApiOutcomeOfSessionEventsReadResponse) => void | Promise<void>;
77
+ }
78
+
79
+ export interface AwaitRunResult {
80
+ state: RunTerminalState;
81
+ cursor: EventCursor;
82
+ page: AgentApiOutcomeOfSessionEventsReadResponse;
83
+ }
84
+
85
+ export class LightspeedClient implements RpcCaller {
86
+ private readonly endpoint: string;
87
+ private readonly fetchImpl: typeof fetch;
88
+ private readonly headers: LightspeedClientOptions["headers"];
89
+ private readonly requestId: (() => RequestId) | undefined;
90
+ private nextNumericRequestId = 1;
91
+
92
+ constructor(endpoint: string | URL);
93
+ constructor(options: LightspeedClientOptions);
94
+ constructor(options: string | URL | LightspeedClientOptions) {
95
+ const resolved =
96
+ typeof options === "string" || options instanceof URL ? { endpoint: options } : options;
97
+ const fetchImpl = resolved.fetch ?? globalThis.fetch;
98
+ if (!fetchImpl) {
99
+ throw new LightspeedTransportError("LightspeedClient requires a fetch implementation");
100
+ }
101
+ this.endpoint = resolved.endpoint.toString();
102
+ this.fetchImpl = fetchImpl;
103
+ this.headers = resolved.headers;
104
+ this.requestId = resolved.requestId;
105
+ }
106
+
107
+ async call<M extends Method>(
108
+ method: M,
109
+ params: MethodParams<M>,
110
+ options: CallOptions = {},
111
+ ): Promise<MethodResult<M>> {
112
+ const id = this.nextRequestId();
113
+ const headers = await this.buildHeaders(options.headers);
114
+ const init: RequestInit = {
115
+ method: "POST",
116
+ headers,
117
+ body: JSON.stringify({ id, method, params }),
118
+ };
119
+ if (options.signal) {
120
+ init.signal = options.signal;
121
+ }
122
+
123
+ let response: Response;
124
+ try {
125
+ response = await this.fetchImpl(this.endpoint, init);
126
+ } catch (error) {
127
+ throw new LightspeedTransportError(`Lightspeed API request failed: ${errorMessage(error)}`);
128
+ }
129
+
130
+ const body = await readJson(response);
131
+ if (!response.ok) {
132
+ throw new LightspeedTransportError(`Lightspeed API request failed with HTTP ${response.status}`, {
133
+ status: response.status,
134
+ body,
135
+ });
136
+ }
137
+ if (!isRecord(body)) {
138
+ throw new LightspeedTransportError("Lightspeed API response was not a JSON object", { body });
139
+ }
140
+
141
+ const rpcResponse = body as JsonRpcResponse;
142
+ if (rpcResponse.id !== undefined && rpcResponse.id !== id) {
143
+ throw new LightspeedTransportError("Lightspeed API response id did not match the request id", {
144
+ body,
145
+ });
146
+ }
147
+ if (rpcResponse.error) {
148
+ throw new LightspeedRpcError(rpcResponse.error);
149
+ }
150
+ if (!("result" in rpcResponse)) {
151
+ throw new LightspeedTransportError("Lightspeed API response was missing result", { body });
152
+ }
153
+ return rpcResponse.result as MethodResult<M>;
154
+ }
155
+
156
+ startRun(
157
+ sessionId: string,
158
+ input: InputItem[],
159
+ options: StartRunOptions = {},
160
+ ): Promise<AgentApiOutcomeOfRunStartResponse> {
161
+ return this.startRunWithSource(sessionId, { type: "input", items: input }, options);
162
+ }
163
+
164
+ startRunWithSource(
165
+ sessionId: string,
166
+ source: RunStartSource,
167
+ options: StartRunOptions = {},
168
+ ): Promise<AgentApiOutcomeOfRunStartResponse> {
169
+ const params: RunStartParams = {
170
+ sessionId,
171
+ source,
172
+ submissionId:
173
+ options.submissionId === undefined ? generateSubmissionId() : options.submissionId,
174
+ };
175
+ if ("config" in options) {
176
+ params.config = options.config ?? null;
177
+ }
178
+ return this.call("session/runs/start", params, options);
179
+ }
180
+
181
+ readEvents(
182
+ sessionId: string,
183
+ options: ReadEventsOptions = {},
184
+ ): Promise<AgentApiOutcomeOfSessionEventsReadResponse> {
185
+ const params: SessionEventsReadParams = { sessionId };
186
+ if (options.after !== undefined) {
187
+ params.after = options.after;
188
+ }
189
+ if (options.limit !== undefined) {
190
+ params.limit = options.limit;
191
+ }
192
+ if (options.waitMs !== undefined) {
193
+ params.waitMs = options.waitMs;
194
+ }
195
+ return this.call("session/events/read", params, options);
196
+ }
197
+
198
+ async awaitRun(
199
+ sessionId: string,
200
+ runId: string,
201
+ options: AwaitRunOptions = {},
202
+ ): Promise<AwaitRunResult> {
203
+ let cursor = options.after ?? null;
204
+ for (;;) {
205
+ throwIfAborted(options.signal);
206
+ const readOptions: ReadEventsOptions = {
207
+ after: cursor,
208
+ waitMs: options.waitMs ?? 30_000,
209
+ };
210
+ if (options.limit !== undefined) {
211
+ readOptions.limit = options.limit;
212
+ }
213
+ if (options.signal) {
214
+ readOptions.signal = options.signal;
215
+ }
216
+ if (options.headers) {
217
+ readOptions.headers = options.headers;
218
+ }
219
+ const page = await this.readEvents(sessionId, readOptions);
220
+ await options.onPage?.(page);
221
+
222
+ for (const event of page.result.events ?? []) {
223
+ cursor = event.cursor;
224
+ await options.onEvent?.(event);
225
+ const state = terminalStateForRun(event, runId);
226
+ if (state) {
227
+ await options.heartbeat?.(cursor, page);
228
+ return { state, cursor, page };
229
+ }
230
+ }
231
+
232
+ cursor = page.result.nextCursor ?? page.result.headCursor ?? cursor;
233
+ await options.heartbeat?.(cursor, page);
234
+ }
235
+ }
236
+
237
+ private nextRequestId(): RequestId {
238
+ if (this.requestId) {
239
+ return this.requestId();
240
+ }
241
+ return this.nextNumericRequestId++;
242
+ }
243
+
244
+ private async buildHeaders(callHeaders: HeadersInit | undefined): Promise<Headers> {
245
+ const headers = new Headers();
246
+ headers.set("content-type", "application/json");
247
+ headers.set("accept", "application/json");
248
+ const defaultHeaders =
249
+ typeof this.headers === "function" ? await this.headers() : this.headers;
250
+ appendHeaders(headers, defaultHeaders);
251
+ appendHeaders(headers, callHeaders);
252
+ return headers;
253
+ }
254
+ }
255
+
256
+ export function generateSubmissionId(): string {
257
+ if (globalThis.crypto?.randomUUID) {
258
+ return globalThis.crypto.randomUUID();
259
+ }
260
+ const bytes = new Uint8Array(16);
261
+ globalThis.crypto?.getRandomValues(bytes);
262
+ if (bytes.some((byte) => byte !== 0)) {
263
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
264
+ }
265
+ return `sub_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
266
+ }
267
+
268
+ function terminalStateForRun(event: SessionEventView, runId: string): RunTerminalState | null {
269
+ const kind = event.kind;
270
+ switch (kind.type) {
271
+ case "runCompleted":
272
+ return kind.runId === runId
273
+ ? { status: "completed", event, outputRef: kind.outputRef ?? null }
274
+ : null;
275
+ case "runFailed":
276
+ return kind.runId === runId ? { status: "failed", event, message: kind.message } : null;
277
+ case "runCancelled":
278
+ return kind.runId === runId ? { status: "cancelled", event } : null;
279
+ default:
280
+ return null;
281
+ }
282
+ }
283
+
284
+ function appendHeaders(headers: Headers, values: HeadersInit | undefined): void {
285
+ if (!values) {
286
+ return;
287
+ }
288
+ new Headers(values).forEach((value, key) => headers.set(key, value));
289
+ }
290
+
291
+ async function readJson(response: Response): Promise<unknown> {
292
+ const text = await response.text();
293
+ if (text.length === 0) {
294
+ return null;
295
+ }
296
+ try {
297
+ return JSON.parse(text) as unknown;
298
+ } catch (error) {
299
+ throw new LightspeedTransportError(`Lightspeed API response was not valid JSON: ${errorMessage(error)}`, {
300
+ status: response.status,
301
+ body: text,
302
+ });
303
+ }
304
+ }
305
+
306
+ function isRecord(value: unknown): value is Record<string, unknown> {
307
+ return typeof value === "object" && value !== null && !Array.isArray(value);
308
+ }
309
+
310
+ function errorMessage(error: unknown): string {
311
+ return error instanceof Error ? error.message : String(error);
312
+ }
313
+
314
+ function throwIfAborted(signal: AbortSignal | undefined): void {
315
+ if (signal?.aborted) {
316
+ throw signal.reason instanceof Error ? signal.reason : new Error("operation aborted");
317
+ }
318
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,64 @@
1
+ import type { AgentApiError, JsonRpcError as ApiJsonRpcError } from "./generated/types.js";
2
+
3
+ export type JsonRpcErrorPayload = ApiJsonRpcError;
4
+
5
+ export type LightspeedRpcErrorKind =
6
+ | "invalid_request"
7
+ | "method_not_found"
8
+ | "not_found"
9
+ | "conflict"
10
+ | "rejected"
11
+ | "environment_not_ready"
12
+ | "internal"
13
+ | "unknown";
14
+
15
+ export function lightspeedRpcErrorKind(code: number): LightspeedRpcErrorKind {
16
+ switch (code) {
17
+ case -32602:
18
+ return "invalid_request";
19
+ case -32601:
20
+ return "method_not_found";
21
+ case -32004:
22
+ return "not_found";
23
+ case -32009:
24
+ return "conflict";
25
+ case -32010:
26
+ return "rejected";
27
+ case -32012:
28
+ return "environment_not_ready";
29
+ case -32603:
30
+ return "internal";
31
+ default:
32
+ return "unknown";
33
+ }
34
+ }
35
+
36
+ export class LightspeedRpcError extends Error {
37
+ readonly code: number;
38
+ readonly data: AgentApiError | null | undefined;
39
+ readonly kind: LightspeedRpcErrorKind;
40
+ readonly payload: JsonRpcErrorPayload;
41
+
42
+ constructor(payload: JsonRpcErrorPayload) {
43
+ super(payload.message);
44
+ this.name = "LightspeedRpcError";
45
+ this.code = payload.code;
46
+ this.data = payload.data;
47
+ this.kind = lightspeedRpcErrorKind(payload.code);
48
+ this.payload = payload;
49
+ Object.setPrototypeOf(this, new.target.prototype);
50
+ }
51
+ }
52
+
53
+ export class LightspeedTransportError extends Error {
54
+ readonly status: number | undefined;
55
+ readonly body: unknown | undefined;
56
+
57
+ constructor(message: string, options: { status?: number; body?: unknown } = {}) {
58
+ super(message);
59
+ this.name = "LightspeedTransportError";
60
+ this.status = options.status;
61
+ this.body = options.body;
62
+ Object.setPrototypeOf(this, new.target.prototype);
63
+ }
64
+ }