@mondaydotcomorg/atp-client 0.19.7 → 0.19.9

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,8 +1,1183 @@
1
- export * from './session.js';
2
- export * from './in-process-session.js';
3
- export * from './api-operations.js';
4
- export * from './execution-operations.js';
5
- export * from './service-providers.js';
6
- export * from './types.js';
1
+ import { ExecutionStatus, CallbackType, ToolOperation } from '@mondaydotcomorg/atp-protocol';
7
2
  export { CallbackType } from '@mondaydotcomorg/atp-protocol';
3
+ import { log } from '@mondaydotcomorg/atp-runtime';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+
14
+ // src/core/session.ts
15
+ var ClientSession = class {
16
+ static {
17
+ __name(this, "ClientSession");
18
+ }
19
+ baseUrl;
20
+ customHeaders;
21
+ clientId;
22
+ clientToken;
23
+ initPromise;
24
+ hooks;
25
+ constructor(baseUrl, headers, hooks) {
26
+ this.baseUrl = baseUrl;
27
+ this.customHeaders = headers || {};
28
+ this.hooks = hooks;
29
+ }
30
+ /**
31
+ * Initializes the client session with the server.
32
+ * This MUST be called before any other operations.
33
+ * The server generates and returns a unique client ID and token.
34
+ * @param clientInfo - Optional client information
35
+ * @param tools - Optional client tool definitions to register with the server
36
+ * @param services - Optional client service capabilities (LLM, approval, embedding)
37
+ */
38
+ async init(clientInfo, tools, services) {
39
+ if (this.initPromise) {
40
+ await this.initPromise;
41
+ return {
42
+ clientId: this.clientId,
43
+ token: this.clientToken,
44
+ expiresAt: 0,
45
+ tokenRotateAt: 0
46
+ };
47
+ }
48
+ this.initPromise = (async () => {
49
+ const url = `${this.baseUrl}/api/init`;
50
+ const body = JSON.stringify({
51
+ clientInfo,
52
+ tools: tools || [],
53
+ services
54
+ });
55
+ const headers = await this.prepareHeaders("POST", url, body);
56
+ const response = await fetch(url, {
57
+ method: "POST",
58
+ headers,
59
+ body
60
+ });
61
+ if (!response.ok) {
62
+ throw new Error(`Client initialization failed: ${response.status} ${response.statusText}`);
63
+ }
64
+ const data = await response.json();
65
+ this.clientId = data.clientId;
66
+ this.clientToken = data.token;
67
+ })();
68
+ await this.initPromise;
69
+ return {
70
+ clientId: this.clientId,
71
+ token: this.clientToken,
72
+ expiresAt: 0,
73
+ tokenRotateAt: 0
74
+ };
75
+ }
76
+ /**
77
+ * Gets the unique client ID.
78
+ */
79
+ getClientId() {
80
+ if (!this.clientId) {
81
+ throw new Error("Client not initialized. Call init() first.");
82
+ }
83
+ return this.clientId;
84
+ }
85
+ /**
86
+ * Ensures the client is initialized before making requests.
87
+ */
88
+ async ensureInitialized() {
89
+ if (!this.clientId) {
90
+ throw new Error("Client not initialized. Call init() first.");
91
+ }
92
+ }
93
+ /**
94
+ * Creates HTTP headers for requests.
95
+ */
96
+ getHeaders() {
97
+ const headers = {
98
+ "Content-Type": "application/json",
99
+ ...this.customHeaders
100
+ };
101
+ if (this.clientId) {
102
+ headers["X-Client-ID"] = this.clientId;
103
+ }
104
+ if (this.clientToken) {
105
+ headers["Authorization"] = `Bearer ${this.clientToken}`;
106
+ }
107
+ return headers;
108
+ }
109
+ getBaseUrl() {
110
+ return this.baseUrl;
111
+ }
112
+ /**
113
+ * Updates the client token from response headers (token refresh).
114
+ */
115
+ updateToken(response) {
116
+ const newToken = response.headers.get("X-ATP-Token");
117
+ if (newToken) {
118
+ this.clientToken = newToken;
119
+ }
120
+ }
121
+ /**
122
+ * Prepares headers for a request, calling preRequest hook if configured
123
+ */
124
+ async prepareHeaders(method, url, body) {
125
+ let headers = {
126
+ "Content-Type": "application/json",
127
+ ...this.customHeaders
128
+ };
129
+ if (this.clientId) {
130
+ headers["X-Client-ID"] = this.clientId;
131
+ }
132
+ if (this.clientToken) {
133
+ headers["Authorization"] = `Bearer ${this.clientToken}`;
134
+ }
135
+ if (this.hooks?.preRequest) {
136
+ try {
137
+ const result = await this.hooks.preRequest({
138
+ url,
139
+ method,
140
+ currentHeaders: headers,
141
+ body
142
+ });
143
+ if (result.abort) {
144
+ throw new Error(result.abortReason || "Request aborted by preRequest hook");
145
+ }
146
+ if (result.headers) {
147
+ headers = result.headers;
148
+ }
149
+ } catch (error) {
150
+ throw error;
151
+ }
152
+ }
153
+ return headers;
154
+ }
155
+ };
156
+
157
+ // src/core/in-process-session.ts
158
+ var InProcessSession = class {
159
+ static {
160
+ __name(this, "InProcessSession");
161
+ }
162
+ server;
163
+ clientId;
164
+ clientToken;
165
+ initialized = false;
166
+ initPromise;
167
+ constructor(server) {
168
+ this.server = server;
169
+ }
170
+ async init(clientInfo, tools, services) {
171
+ if (this.initPromise) {
172
+ await this.initPromise;
173
+ return {
174
+ clientId: this.clientId,
175
+ token: this.clientToken,
176
+ expiresAt: 0,
177
+ tokenRotateAt: 0
178
+ };
179
+ }
180
+ this.initPromise = (async () => {
181
+ await this.server.start();
182
+ const ctx = this.createContext({
183
+ method: "POST",
184
+ path: "/api/init",
185
+ body: {
186
+ clientInfo,
187
+ tools: tools || [],
188
+ services
189
+ }
190
+ });
191
+ const result = await this.server.handleInit(ctx);
192
+ this.clientId = result.clientId;
193
+ this.clientToken = result.token;
194
+ this.initialized = true;
195
+ })();
196
+ await this.initPromise;
197
+ return {
198
+ clientId: this.clientId,
199
+ token: this.clientToken,
200
+ expiresAt: 0,
201
+ tokenRotateAt: 0
202
+ };
203
+ }
204
+ getClientId() {
205
+ if (!this.clientId) {
206
+ throw new Error("Client not initialized. Call init() first.");
207
+ }
208
+ return this.clientId;
209
+ }
210
+ async ensureInitialized() {
211
+ if (!this.initialized) {
212
+ throw new Error("Client not initialized. Call init() first.");
213
+ }
214
+ }
215
+ getHeaders() {
216
+ const headers = {
217
+ "content-type": "application/json"
218
+ };
219
+ if (this.clientId) {
220
+ headers["x-client-id"] = this.clientId;
221
+ }
222
+ if (this.clientToken) {
223
+ headers["authorization"] = `Bearer ${this.clientToken}`;
224
+ }
225
+ return headers;
226
+ }
227
+ getBaseUrl() {
228
+ return "";
229
+ }
230
+ updateToken(_response) {
231
+ }
232
+ async prepareHeaders(_method, _url, _body) {
233
+ return this.getHeaders();
234
+ }
235
+ async getDefinitions(options) {
236
+ await this.ensureInitialized();
237
+ const ctx = this.createContext({
238
+ method: "GET",
239
+ path: "/api/definitions",
240
+ query: options?.apiGroups ? {
241
+ apiGroups: options.apiGroups.join(",")
242
+ } : {}
243
+ });
244
+ return await this.server.getDefinitions(ctx);
245
+ }
246
+ async getRuntimeDefinitions(options) {
247
+ await this.ensureInitialized();
248
+ const ctx = this.createContext({
249
+ method: "GET",
250
+ path: "/api/runtime",
251
+ query: options?.apis?.length ? {
252
+ apis: options.apis.join(",")
253
+ } : {}
254
+ });
255
+ return await this.server.getRuntimeDefinitions(ctx);
256
+ }
257
+ async getServerInfo() {
258
+ await this.ensureInitialized();
259
+ return this.server.getInfo();
260
+ }
261
+ async search(query, options) {
262
+ await this.ensureInitialized();
263
+ const ctx = this.createContext({
264
+ method: "POST",
265
+ path: "/api/search",
266
+ body: {
267
+ query,
268
+ ...options
269
+ }
270
+ });
271
+ return await this.server.handleSearch(ctx);
272
+ }
273
+ async explore(path) {
274
+ await this.ensureInitialized();
275
+ const ctx = this.createContext({
276
+ method: "POST",
277
+ path: "/api/explore",
278
+ body: {
279
+ path
280
+ }
281
+ });
282
+ return await this.server.handleExplore(ctx);
283
+ }
284
+ async execute(code, config) {
285
+ await this.ensureInitialized();
286
+ const ctx = this.createContext({
287
+ method: "POST",
288
+ path: "/api/execute",
289
+ body: {
290
+ code,
291
+ config
292
+ }
293
+ });
294
+ return await this.server.handleExecute(ctx);
295
+ }
296
+ async resume(executionId, callbackResult) {
297
+ await this.ensureInitialized();
298
+ const ctx = this.createContext({
299
+ method: "POST",
300
+ path: `/api/resume/${executionId}`,
301
+ body: {
302
+ result: callbackResult
303
+ }
304
+ });
305
+ return await this.server.handleResume(ctx, executionId);
306
+ }
307
+ async resumeWithBatchResults(executionId, batchResults) {
308
+ await this.ensureInitialized();
309
+ const ctx = this.createContext({
310
+ method: "POST",
311
+ path: `/api/resume/${executionId}`,
312
+ body: {
313
+ results: batchResults
314
+ }
315
+ });
316
+ return await this.server.handleResume(ctx, executionId);
317
+ }
318
+ createContext(options) {
319
+ const noopLogger = {
320
+ debug: /* @__PURE__ */ __name(() => {
321
+ }, "debug"),
322
+ info: /* @__PURE__ */ __name(() => {
323
+ }, "info"),
324
+ warn: /* @__PURE__ */ __name(() => {
325
+ }, "warn"),
326
+ error: /* @__PURE__ */ __name(() => {
327
+ }, "error")
328
+ };
329
+ return {
330
+ method: options.method,
331
+ path: options.path,
332
+ query: options.query || {},
333
+ headers: this.getHeaders(),
334
+ body: options.body,
335
+ clientId: this.clientId,
336
+ clientToken: this.clientToken,
337
+ logger: noopLogger,
338
+ status: 200,
339
+ responseBody: null,
340
+ throw: /* @__PURE__ */ __name((status, message) => {
341
+ const error = new Error(message);
342
+ error.status = status;
343
+ throw error;
344
+ }, "throw"),
345
+ assert: /* @__PURE__ */ __name((condition, message) => {
346
+ if (!condition) {
347
+ throw new Error(message);
348
+ }
349
+ }, "assert"),
350
+ set: /* @__PURE__ */ __name(() => {
351
+ }, "set")
352
+ };
353
+ }
354
+ };
355
+
356
+ // src/core/api-operations.ts
357
+ var APIOperations = class {
358
+ static {
359
+ __name(this, "APIOperations");
360
+ }
361
+ session;
362
+ inProcessSession;
363
+ apiDefinitions;
364
+ constructor(session, inProcessSession) {
365
+ this.session = session;
366
+ this.inProcessSession = inProcessSession;
367
+ }
368
+ /**
369
+ * Connects to the server and retrieves API definitions.
370
+ */
371
+ async connect(options) {
372
+ await this.session.ensureInitialized();
373
+ if (this.inProcessSession) {
374
+ const data2 = await this.inProcessSession.getDefinitions(options);
375
+ this.apiDefinitions = data2.typescript;
376
+ return {
377
+ serverVersion: data2.version,
378
+ capabilities: {},
379
+ apiGroups: data2.apiGroups
380
+ };
381
+ }
382
+ const params = new URLSearchParams();
383
+ if (options?.apiGroups) {
384
+ params.set("apiGroups", options.apiGroups.join(","));
385
+ }
386
+ const url = `${this.session.getBaseUrl()}/api/definitions?${params}`;
387
+ const headers = await this.session.prepareHeaders("GET", url);
388
+ const response = await fetch(url, {
389
+ headers
390
+ });
391
+ if (!response.ok) {
392
+ throw new Error(`Connection failed: ${response.status} ${response.statusText}`);
393
+ }
394
+ const data = await response.json();
395
+ this.apiDefinitions = data.typescript;
396
+ return {
397
+ serverVersion: data.version,
398
+ capabilities: {},
399
+ apiGroups: data.apiGroups
400
+ };
401
+ }
402
+ /**
403
+ * Gets the TypeScript type definitions for available APIs.
404
+ */
405
+ getTypeDefinitions() {
406
+ if (!this.apiDefinitions) {
407
+ throw new Error("Not connected. Call connect() first.");
408
+ }
409
+ return this.apiDefinitions;
410
+ }
411
+ /**
412
+ * Searches for available API functions.
413
+ */
414
+ async searchAPI(query, options) {
415
+ await this.session.ensureInitialized();
416
+ if (this.inProcessSession) {
417
+ const data2 = await this.inProcessSession.search(query, options);
418
+ return data2.results;
419
+ }
420
+ const url = `${this.session.getBaseUrl()}/api/search`;
421
+ const body = JSON.stringify({
422
+ query,
423
+ ...options
424
+ });
425
+ const headers = await this.session.prepareHeaders("POST", url, body);
426
+ const response = await fetch(url, {
427
+ method: "POST",
428
+ headers,
429
+ body
430
+ });
431
+ if (!response.ok) {
432
+ throw new Error(`Search failed: ${response.status} ${response.statusText}`);
433
+ }
434
+ const data = await response.json();
435
+ return data.results;
436
+ }
437
+ /**
438
+ * Explores the API filesystem at the given path.
439
+ */
440
+ async exploreAPI(path) {
441
+ await this.session.ensureInitialized();
442
+ if (this.inProcessSession) {
443
+ return await this.inProcessSession.explore(path);
444
+ }
445
+ const url = `${this.session.getBaseUrl()}/api/explore`;
446
+ const body = JSON.stringify({
447
+ path
448
+ });
449
+ const headers = await this.session.prepareHeaders("POST", url, body);
450
+ const response = await fetch(url, {
451
+ method: "POST",
452
+ headers,
453
+ body
454
+ });
455
+ if (!response.ok) {
456
+ throw new Error(`Explore failed: ${response.status} ${response.statusText}`);
457
+ }
458
+ return await response.json();
459
+ }
460
+ /**
461
+ * Gets information about the server.
462
+ */
463
+ async getServerInfo() {
464
+ await this.session.ensureInitialized();
465
+ if (this.inProcessSession) {
466
+ return await this.inProcessSession.getServerInfo();
467
+ }
468
+ const url = `${this.session.getBaseUrl()}/api/info`;
469
+ const headers = await this.session.prepareHeaders("GET", url);
470
+ const response = await fetch(url, {
471
+ headers
472
+ });
473
+ if (!response.ok) {
474
+ throw new Error(`Failed to get server info: ${response.status}`);
475
+ }
476
+ return await response.json();
477
+ }
478
+ /**
479
+ * Gets ATP runtime API definitions as TypeScript declarations.
480
+ * Returns the full TypeScript definitions for atp.llm.*, atp.cache.*, etc.
481
+ * These are the APIs available during code execution.
482
+ *
483
+ * Behavior:
484
+ * - No options: Returns APIs based on client capabilities (default filtering)
485
+ * - apis: ['llm', 'cache']: Returns only specified APIs (intersection with client capabilities)
486
+ * - apis: []: Returns all APIs regardless of client capabilities
487
+ *
488
+ * @param options - Optional filtering options
489
+ * @param options.apis - Specific APIs to include (e.g., ['llm', 'cache', 'approval'])
490
+ */
491
+ async getRuntimeDefinitions(options) {
492
+ await this.session.ensureInitialized();
493
+ if (this.inProcessSession) {
494
+ return await this.inProcessSession.getRuntimeDefinitions(options?.apis ? {
495
+ apis: options.apis
496
+ } : void 0);
497
+ }
498
+ const params = new URLSearchParams();
499
+ if (options?.apis && options.apis.length > 0) {
500
+ params.set("apis", options.apis.join(","));
501
+ }
502
+ const url = `${this.session.getBaseUrl()}/api/runtime${params.toString() ? `?${params}` : ""}`;
503
+ const headers = await this.session.prepareHeaders("GET", url);
504
+ const response = await fetch(url, {
505
+ headers
506
+ });
507
+ if (!response.ok) {
508
+ throw new Error(`Failed to get runtime definitions: ${response.status}`);
509
+ }
510
+ return await response.text();
511
+ }
512
+ };
513
+
514
+ // src/errors.ts
515
+ var ClientCallbackError = class extends Error {
516
+ static {
517
+ __name(this, "ClientCallbackError");
518
+ }
519
+ constructor(message) {
520
+ super(message);
521
+ this.name = "ClientCallbackError";
522
+ }
523
+ };
524
+
525
+ // src/core/provenance-registry.ts
526
+ var ProvenanceTokenRegistry = class {
527
+ static {
528
+ __name(this, "ProvenanceTokenRegistry");
529
+ }
530
+ cache = /* @__PURE__ */ new Map();
531
+ maxSize;
532
+ ttl;
533
+ sequenceCounter = 0;
534
+ constructor(maxSize = 1e4, ttlHours = 1) {
535
+ this.maxSize = maxSize;
536
+ this.ttl = ttlHours * 3600 * 1e3;
537
+ }
538
+ /**
539
+ * Add a token to the registry
540
+ */
541
+ add(token) {
542
+ this.evictExpired();
543
+ if (this.cache.size >= this.maxSize) {
544
+ this.evictLRU();
545
+ }
546
+ this.cache.set(token, {
547
+ token,
548
+ addedAt: Date.now(),
549
+ sequence: this.sequenceCounter++
550
+ });
551
+ }
552
+ /**
553
+ * Get recent tokens (non-expired, sorted by age, limited)
554
+ * Returns tokens in chronological order (oldest first, most recent last)
555
+ */
556
+ getRecentTokens(maxCount = 1e3) {
557
+ if (maxCount <= 0) {
558
+ return [];
559
+ }
560
+ this.evictExpired();
561
+ const now = Date.now();
562
+ const expiredTokens = [];
563
+ const entries = Array.from(this.cache.values()).filter((entry) => {
564
+ try {
565
+ const [body] = entry.token.split(".");
566
+ if (!body) {
567
+ expiredTokens.push(entry.token);
568
+ return false;
569
+ }
570
+ const payload = JSON.parse(Buffer.from(body, "base64url").toString());
571
+ if (!payload.expiresAt || payload.expiresAt <= now) {
572
+ expiredTokens.push(entry.token);
573
+ return false;
574
+ }
575
+ return true;
576
+ } catch {
577
+ expiredTokens.push(entry.token);
578
+ return false;
579
+ }
580
+ }).sort((a, b) => a.sequence - b.sequence).slice(-maxCount);
581
+ for (const token of expiredTokens) {
582
+ this.cache.delete(token);
583
+ }
584
+ return entries.map((e) => e.token);
585
+ }
586
+ /**
587
+ * Clear all tokens
588
+ */
589
+ clear() {
590
+ this.cache.clear();
591
+ }
592
+ /**
593
+ * Get registry size
594
+ */
595
+ size() {
596
+ return this.cache.size;
597
+ }
598
+ /**
599
+ * Evict expired tokens
600
+ */
601
+ evictExpired() {
602
+ const now = Date.now();
603
+ const toDelete = [];
604
+ for (const [token, entry] of this.cache.entries()) {
605
+ if (now - entry.addedAt > this.ttl) {
606
+ toDelete.push(token);
607
+ }
608
+ }
609
+ for (const token of toDelete) {
610
+ this.cache.delete(token);
611
+ }
612
+ }
613
+ /**
614
+ * Evict least recently used (oldest) token
615
+ */
616
+ evictLRU() {
617
+ let oldestToken = null;
618
+ let oldestSequence = Infinity;
619
+ for (const [token, entry] of this.cache.entries()) {
620
+ if (entry.sequence < oldestSequence) {
621
+ oldestSequence = entry.sequence;
622
+ oldestToken = token;
623
+ }
624
+ }
625
+ if (oldestToken) {
626
+ this.cache.delete(oldestToken);
627
+ }
628
+ }
629
+ };
630
+
631
+ // src/core/execution-operations.ts
632
+ var ExecutionOperations = class {
633
+ static {
634
+ __name(this, "ExecutionOperations");
635
+ }
636
+ session;
637
+ inProcessSession;
638
+ serviceProviders;
639
+ tokenRegistry;
640
+ lastExecutionConfig = null;
641
+ constructor(session, serviceProviders, inProcessSession) {
642
+ this.session = session;
643
+ this.inProcessSession = inProcessSession;
644
+ this.serviceProviders = serviceProviders;
645
+ this.tokenRegistry = new ProvenanceTokenRegistry();
646
+ }
647
+ /**
648
+ * Executes code on the server with real-time progress updates via SSE.
649
+ */
650
+ async executeStream(code, config, onProgress) {
651
+ await this.session.ensureInitialized();
652
+ const url = `${this.session.getBaseUrl()}/api/execute/stream`;
653
+ const body = JSON.stringify({
654
+ code,
655
+ config
656
+ });
657
+ const headers = await this.session.prepareHeaders("POST", url, body);
658
+ return new Promise((resolve, reject) => {
659
+ const fetchImpl = typeof fetch !== "undefined" ? fetch : __require("undici").fetch;
660
+ fetchImpl(url, {
661
+ method: "POST",
662
+ headers,
663
+ body
664
+ }).then(async (response) => {
665
+ if (!response.ok) {
666
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
667
+ }
668
+ const reader = response.body?.getReader();
669
+ if (!reader) {
670
+ throw new Error("Response body is not readable");
671
+ }
672
+ const decoder = new TextDecoder();
673
+ let buffer = "";
674
+ let result = null;
675
+ while (true) {
676
+ const { done, value } = await reader.read();
677
+ if (done) break;
678
+ buffer += decoder.decode(value, {
679
+ stream: true
680
+ });
681
+ const lines = buffer.split("\n");
682
+ buffer = lines.pop() || "";
683
+ for (let i = 0; i < lines.length; i++) {
684
+ const line = lines[i];
685
+ if (line && line.startsWith("event:")) {
686
+ const event = line.substring(6).trim();
687
+ for (let j = i + 1; j < lines.length; j++) {
688
+ const dataLine = lines[j];
689
+ if (dataLine && dataLine.startsWith("data:")) {
690
+ const dataStr = dataLine.substring(5).trim();
691
+ if (dataStr) {
692
+ try {
693
+ const data = JSON.parse(dataStr);
694
+ if (event === "progress" && onProgress) {
695
+ onProgress(data.message, data.fraction);
696
+ } else if (event === "result") {
697
+ result = data;
698
+ } else if (event === "error") {
699
+ reject(new Error(data.message));
700
+ return;
701
+ }
702
+ } catch (e) {
703
+ log.error("Failed to parse SSE data", {
704
+ dataStr,
705
+ error: e
706
+ });
707
+ }
708
+ }
709
+ break;
710
+ }
711
+ }
712
+ }
713
+ }
714
+ }
715
+ if (result) {
716
+ resolve(result);
717
+ } else {
718
+ reject(new Error("No result received from server"));
719
+ }
720
+ }).catch(reject);
721
+ });
722
+ }
723
+ /**
724
+ * Executes code on the server in a sandboxed environment.
725
+ */
726
+ async execute(code, config) {
727
+ await this.session.ensureInitialized();
728
+ const hints = this.tokenRegistry.getRecentTokens(1e3);
729
+ const detectedClientServices = {
730
+ hasLLM: !!this.serviceProviders.getLLM(),
731
+ hasApproval: !!this.serviceProviders.getApproval(),
732
+ hasEmbedding: !!this.serviceProviders.getEmbedding(),
733
+ hasTools: this.serviceProviders.hasTools()
734
+ };
735
+ const executionConfig = {
736
+ ...config,
737
+ clientServices: {
738
+ ...detectedClientServices,
739
+ ...config?.clientServices || {}
740
+ },
741
+ provenanceHints: hints.length > 0 ? hints : void 0
742
+ };
743
+ this.lastExecutionConfig = executionConfig;
744
+ let result;
745
+ if (this.inProcessSession) {
746
+ result = await this.inProcessSession.execute(code, executionConfig);
747
+ } else {
748
+ const url = `${this.session.getBaseUrl()}/api/execute`;
749
+ const body = JSON.stringify({
750
+ code,
751
+ config: executionConfig
752
+ });
753
+ const headers = await this.session.prepareHeaders("POST", url, body);
754
+ const response = await fetch(url, {
755
+ method: "POST",
756
+ headers,
757
+ body
758
+ });
759
+ this.session.updateToken(response);
760
+ if (!response.ok) {
761
+ const error = await response.json();
762
+ throw new Error(`Execution failed: ${error.error || response.statusText}`);
763
+ }
764
+ result = await response.json();
765
+ }
766
+ if (result.provenanceTokens && result.provenanceTokens.length > 0) {
767
+ for (const { token } of result.provenanceTokens) {
768
+ this.tokenRegistry.add(token);
769
+ }
770
+ }
771
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallbacks) {
772
+ return await this.handleBatchCallbacksAndResume(result);
773
+ }
774
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallback) {
775
+ return await this.handlePauseAndResume(result);
776
+ }
777
+ return result;
778
+ }
779
+ /**
780
+ * Handles batch callbacks by executing them in parallel and resuming.
781
+ */
782
+ async handleBatchCallbacksAndResume(pausedResult) {
783
+ if (!pausedResult.needsCallbacks || pausedResult.needsCallbacks.length === 0) {
784
+ throw new Error("No batch callback requests in paused execution");
785
+ }
786
+ const missingServiceIds = new Set(pausedResult.needsCallbacks.filter((cb) => !this.serviceProviders.hasServiceForCallback(cb.type)).map((cb) => cb.id));
787
+ if (missingServiceIds.size > 0) {
788
+ const missingServices = pausedResult.needsCallbacks.filter((cb) => missingServiceIds.has(cb.id));
789
+ const explicitlyRequestedMissing = missingServices.filter((cb) => this.wasServiceExplicitlyRequested(cb.type));
790
+ const unexpectedMissing = missingServices.filter((cb) => !this.wasServiceExplicitlyRequested(cb.type));
791
+ if (explicitlyRequestedMissing.length > 0) {
792
+ return pausedResult;
793
+ }
794
+ const errorMessage = `Missing service providers for callback types: ${unexpectedMissing.map((cb) => cb.type).join(", ")}`;
795
+ log.error(`Auto-handling batch paused execution without service providers: ${errorMessage}`, {
796
+ executionId: pausedResult.executionId,
797
+ missingServices: unexpectedMissing.map((cb) => ({
798
+ type: cb.type,
799
+ operation: cb.operation,
800
+ id: cb.id
801
+ }))
802
+ });
803
+ const existingCallbacks = pausedResult.needsCallbacks.filter((cb) => !missingServiceIds.has(cb.id));
804
+ if (existingCallbacks.length > 0) {
805
+ try {
806
+ const existingResults = await Promise.all(existingCallbacks.map(async (cb) => {
807
+ const callbackResult = await this.serviceProviders.handleCallback(cb.type, {
808
+ ...cb.payload,
809
+ operation: cb.operation
810
+ });
811
+ return {
812
+ id: cb.id,
813
+ result: callbackResult
814
+ };
815
+ }));
816
+ const allResults = pausedResult.needsCallbacks.map((cb) => {
817
+ if (missingServiceIds.has(cb.id)) {
818
+ return {
819
+ id: cb.id,
820
+ result: {
821
+ __error: true,
822
+ message: `${cb.type} service not provided by client`
823
+ }
824
+ };
825
+ }
826
+ return existingResults.find((r) => r.id === cb.id);
827
+ });
828
+ return await this.resumeWithBatchResults(pausedResult.executionId, allResults);
829
+ } catch (error) {
830
+ const errorMessage2 = error instanceof Error ? error.message : String(error);
831
+ log.error(`Error handling existing services in batch: ${errorMessage2}`, {
832
+ executionId: pausedResult.executionId
833
+ });
834
+ const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({
835
+ id: cb.id,
836
+ result: {
837
+ __error: true,
838
+ message: missingServiceIds.has(cb.id) ? `${cb.type} service not provided by client` : errorMessage2
839
+ }
840
+ }));
841
+ return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults);
842
+ }
843
+ } else {
844
+ const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({
845
+ id: cb.id,
846
+ result: {
847
+ __error: true,
848
+ message: `${cb.type} service not provided by client`
849
+ }
850
+ }));
851
+ return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults);
852
+ }
853
+ }
854
+ try {
855
+ const batchResults = await Promise.all(pausedResult.needsCallbacks.map(async (cb) => {
856
+ const callbackResult = await this.serviceProviders.handleCallback(cb.type, {
857
+ ...cb.payload,
858
+ operation: cb.operation
859
+ });
860
+ return {
861
+ id: cb.id,
862
+ result: callbackResult
863
+ };
864
+ }));
865
+ return await this.resumeWithBatchResults(pausedResult.executionId, batchResults);
866
+ } catch (error) {
867
+ const errorMessage = error instanceof Error ? error.message : String(error);
868
+ log.error(`Error handling batch callbacks: ${errorMessage}`, {
869
+ executionId: pausedResult.executionId,
870
+ callbackCount: pausedResult.needsCallbacks.length
871
+ });
872
+ const allErrorResults = pausedResult.needsCallbacks.map((cb) => ({
873
+ id: cb.id,
874
+ result: {
875
+ __error: true,
876
+ message: errorMessage
877
+ }
878
+ }));
879
+ return await this.resumeWithBatchResults(pausedResult.executionId, allErrorResults);
880
+ }
881
+ }
882
+ /**
883
+ * Handles a paused execution by processing the callback and resuming.
884
+ */
885
+ async handlePauseAndResume(pausedResult) {
886
+ if (!pausedResult.needsCallback) {
887
+ throw new Error("No callback request in paused execution");
888
+ }
889
+ if (!this.serviceProviders.hasServiceForCallback(pausedResult.needsCallback.type)) {
890
+ const wasExplicitlyRequested = this.wasServiceExplicitlyRequested(pausedResult.needsCallback.type);
891
+ if (wasExplicitlyRequested) {
892
+ return pausedResult;
893
+ }
894
+ const errorMessage = `${pausedResult.needsCallback.type} service not provided by client`;
895
+ log.error(`Auto-handling paused execution without service provider: ${errorMessage}`, {
896
+ executionId: pausedResult.executionId,
897
+ callbackType: pausedResult.needsCallback.type,
898
+ operation: pausedResult.needsCallback.operation
899
+ });
900
+ return await this.resume(pausedResult.executionId, {
901
+ __error: true,
902
+ message: errorMessage
903
+ });
904
+ }
905
+ try {
906
+ const callbackResult = await this.serviceProviders.handleCallback(pausedResult.needsCallback.type, {
907
+ ...pausedResult.needsCallback.payload,
908
+ operation: pausedResult.needsCallback.operation,
909
+ executionId: pausedResult.executionId
910
+ });
911
+ return await this.resume(pausedResult.executionId, callbackResult);
912
+ } catch (error) {
913
+ if (error instanceof ClientCallbackError) {
914
+ throw error;
915
+ }
916
+ const errorMessage = error instanceof Error ? error.message : String(error);
917
+ log.error(`Error handling callback: ${errorMessage}`, {
918
+ executionId: pausedResult.executionId,
919
+ callbackType: pausedResult.needsCallback.type,
920
+ operation: pausedResult.needsCallback.operation
921
+ });
922
+ return await this.resume(pausedResult.executionId, {
923
+ __error: true,
924
+ message: errorMessage
925
+ });
926
+ }
927
+ }
928
+ /**
929
+ * Check if a service was explicitly requested in clientServices config
930
+ */
931
+ wasServiceExplicitlyRequested(callbackType) {
932
+ if (!this.lastExecutionConfig?.clientServices) {
933
+ return false;
934
+ }
935
+ switch (callbackType) {
936
+ case CallbackType.LLM:
937
+ return this.lastExecutionConfig.clientServices.hasLLM;
938
+ case CallbackType.APPROVAL:
939
+ return this.lastExecutionConfig.clientServices.hasApproval;
940
+ case CallbackType.EMBEDDING:
941
+ return this.lastExecutionConfig.clientServices.hasEmbedding;
942
+ case CallbackType.TOOL:
943
+ return this.lastExecutionConfig.clientServices.hasTools;
944
+ default:
945
+ return false;
946
+ }
947
+ }
948
+ /**
949
+ * Resumes a paused execution with a callback result.
950
+ */
951
+ async resume(executionId, callbackResult) {
952
+ await this.session.ensureInitialized();
953
+ let result;
954
+ if (this.inProcessSession) {
955
+ result = await this.inProcessSession.resume(executionId, callbackResult);
956
+ } else {
957
+ const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
958
+ const body = JSON.stringify({
959
+ result: callbackResult
960
+ });
961
+ const headers = await this.session.prepareHeaders("POST", url, body);
962
+ const response = await fetch(url, {
963
+ method: "POST",
964
+ headers,
965
+ body
966
+ });
967
+ this.session.updateToken(response);
968
+ if (!response.ok) {
969
+ const error = await response.json();
970
+ throw new Error(`Resume failed: ${error.error || response.statusText}`);
971
+ }
972
+ result = await response.json();
973
+ }
974
+ if (result.provenanceTokens && result.provenanceTokens.length > 0) {
975
+ for (const { token } of result.provenanceTokens) {
976
+ this.tokenRegistry.add(token);
977
+ }
978
+ }
979
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallbacks) {
980
+ return await this.handleBatchCallbacksAndResume(result);
981
+ }
982
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallback) {
983
+ return await this.handlePauseAndResume(result);
984
+ }
985
+ return result;
986
+ }
987
+ /**
988
+ * Resumes a paused execution with batch callback results.
989
+ */
990
+ async resumeWithBatchResults(executionId, batchResults) {
991
+ await this.session.ensureInitialized();
992
+ let result;
993
+ if (this.inProcessSession) {
994
+ result = await this.inProcessSession.resumeWithBatchResults(executionId, batchResults);
995
+ } else {
996
+ const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
997
+ const body = JSON.stringify({
998
+ results: batchResults
999
+ });
1000
+ const headers = await this.session.prepareHeaders("POST", url, body);
1001
+ const response = await fetch(url, {
1002
+ method: "POST",
1003
+ headers,
1004
+ body
1005
+ });
1006
+ this.session.updateToken(response);
1007
+ if (!response.ok) {
1008
+ const error = await response.json();
1009
+ throw new Error(`Batch resume failed: ${error.error || response.statusText}`);
1010
+ }
1011
+ result = await response.json();
1012
+ }
1013
+ if (result.provenanceTokens && result.provenanceTokens.length > 0) {
1014
+ for (const { token } of result.provenanceTokens) {
1015
+ this.tokenRegistry.add(token);
1016
+ }
1017
+ }
1018
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallbacks) {
1019
+ return await this.handleBatchCallbacksAndResume(result);
1020
+ }
1021
+ if (result.status === ExecutionStatus.PAUSED && result.needsCallback) {
1022
+ return await this.handlePauseAndResume(result);
1023
+ }
1024
+ return result;
1025
+ }
1026
+ };
1027
+ var LLMOperation = {
1028
+ CALL: "call",
1029
+ EXTRACT: "extract",
1030
+ CLASSIFY: "classify"
1031
+ };
1032
+ var EmbeddingOperation = {
1033
+ EMBED: "embed",
1034
+ SEARCH: "search"
1035
+ };
1036
+ var ServiceProviders = class {
1037
+ static {
1038
+ __name(this, "ServiceProviders");
1039
+ }
1040
+ providers = {};
1041
+ toolHandlers = /* @__PURE__ */ new Map();
1042
+ constructor(providers) {
1043
+ this.providers = providers || {};
1044
+ if (providers?.tools) {
1045
+ for (const tool of providers.tools) {
1046
+ this.toolHandlers.set(tool.name, tool.handler);
1047
+ }
1048
+ }
1049
+ }
1050
+ provideLLM(handler) {
1051
+ this.providers.llm = handler;
1052
+ }
1053
+ provideApproval(handler) {
1054
+ this.providers.approval = handler;
1055
+ }
1056
+ provideEmbedding(handler) {
1057
+ this.providers.embedding = handler;
1058
+ }
1059
+ provideTools(tools) {
1060
+ this.providers.tools = tools;
1061
+ for (const tool of tools) {
1062
+ this.toolHandlers.set(tool.name, tool.handler);
1063
+ }
1064
+ }
1065
+ getLLM() {
1066
+ return this.providers.llm;
1067
+ }
1068
+ getApproval() {
1069
+ return this.providers.approval;
1070
+ }
1071
+ getEmbedding() {
1072
+ return this.providers.embedding;
1073
+ }
1074
+ getTools() {
1075
+ return this.providers.tools;
1076
+ }
1077
+ /**
1078
+ * Get tool definitions (without handlers) for sending to server
1079
+ */
1080
+ getToolDefinitions() {
1081
+ if (!this.providers.tools) {
1082
+ return [];
1083
+ }
1084
+ return this.providers.tools.map((tool) => {
1085
+ const { handler, ...definition } = tool;
1086
+ return definition;
1087
+ });
1088
+ }
1089
+ /**
1090
+ * Check if client has tools
1091
+ */
1092
+ hasTools() {
1093
+ return !!(this.providers.tools && this.providers.tools.length > 0);
1094
+ }
1095
+ /**
1096
+ * Check if client has any services or tools
1097
+ */
1098
+ hasAnyServices() {
1099
+ return !!(this.providers.llm || this.providers.approval || this.providers.embedding || this.hasTools());
1100
+ }
1101
+ /**
1102
+ * Check if client has a service for a specific callback type
1103
+ */
1104
+ hasServiceForCallback(callbackType) {
1105
+ switch (callbackType) {
1106
+ case CallbackType.LLM:
1107
+ return !!this.providers.llm;
1108
+ case CallbackType.APPROVAL:
1109
+ return !!this.providers.approval;
1110
+ case CallbackType.EMBEDDING:
1111
+ return !!this.providers.embedding;
1112
+ case CallbackType.TOOL:
1113
+ return this.hasTools();
1114
+ default:
1115
+ return false;
1116
+ }
1117
+ }
1118
+ async handleCallback(callbackType, payload) {
1119
+ if (payload.operation === "batch_parallel" && payload.calls) {
1120
+ return await Promise.all(payload.calls.map(async (call) => {
1121
+ return await this.handleCallback(call.type, {
1122
+ ...call.payload,
1123
+ operation: call.operation
1124
+ });
1125
+ }));
1126
+ }
1127
+ switch (callbackType) {
1128
+ case CallbackType.LLM:
1129
+ if (!this.providers.llm) {
1130
+ throw new Error("LLM service not provided by client");
1131
+ }
1132
+ if (payload.operation === LLMOperation.CALL) {
1133
+ return await this.providers.llm.call(payload.prompt, payload.options);
1134
+ } else if (payload.operation === LLMOperation.EXTRACT && this.providers.llm.extract) {
1135
+ return await this.providers.llm.extract(payload.prompt, payload.schema, payload.options);
1136
+ } else if (payload.operation === LLMOperation.CLASSIFY && this.providers.llm.classify) {
1137
+ return await this.providers.llm.classify(payload.text, payload.categories, payload.options);
1138
+ }
1139
+ throw new Error(`Unsupported LLM operation: ${payload.operation}`);
1140
+ case CallbackType.APPROVAL:
1141
+ if (!this.providers.approval) {
1142
+ throw new Error("Approval service not provided by client");
1143
+ }
1144
+ const contextWithExecutionId = payload.context ? {
1145
+ ...payload.context,
1146
+ executionId: payload.executionId
1147
+ } : {
1148
+ executionId: payload.executionId
1149
+ };
1150
+ return await this.providers.approval.request(payload.message, contextWithExecutionId);
1151
+ case CallbackType.EMBEDDING:
1152
+ if (!this.providers.embedding) {
1153
+ throw new Error("Embedding service not provided by client");
1154
+ }
1155
+ if (payload.operation === EmbeddingOperation.EMBED) {
1156
+ return await this.providers.embedding.embed(payload.text);
1157
+ } else if (payload.operation === EmbeddingOperation.SEARCH) {
1158
+ const queryEmbedding = await this.providers.embedding.embed(payload.query);
1159
+ return queryEmbedding;
1160
+ } else if (payload.operation === "similarity" && this.providers.embedding.similarity) {
1161
+ return await this.providers.embedding.similarity(payload.text1, payload.text2);
1162
+ }
1163
+ throw new Error(`Unsupported embedding operation: ${payload.operation}`);
1164
+ case CallbackType.TOOL:
1165
+ if (payload.operation === ToolOperation.CALL) {
1166
+ const toolName = payload.toolName;
1167
+ const handler = this.toolHandlers.get(toolName);
1168
+ if (!handler) {
1169
+ throw new Error(`Tool '${toolName}' not found in client tools`);
1170
+ }
1171
+ const result = await handler(payload.input);
1172
+ return result;
1173
+ }
1174
+ throw new Error(`Unsupported tool operation: ${payload.operation}`);
1175
+ default:
1176
+ throw new Error(`Unknown callback type: ${callbackType}`);
1177
+ }
1178
+ }
1179
+ };
1180
+
1181
+ export { APIOperations, ClientSession, ExecutionOperations, InProcessSession, ServiceProviders };
1182
+ //# sourceMappingURL=index.js.map
8
1183
  //# sourceMappingURL=index.js.map