@mondaydotcomorg/atp-client 0.19.5 → 0.19.7

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 (40) hide show
  1. package/dist/client.d.ts +23 -2
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +26 -5
  4. package/dist/client.js.map +1 -1
  5. package/dist/core/api-operations.d.ts +4 -2
  6. package/dist/core/api-operations.d.ts.map +1 -1
  7. package/dist/core/api-operations.js +25 -1
  8. package/dist/core/api-operations.js.map +1 -1
  9. package/dist/core/execution-operations.d.ts +4 -2
  10. package/dist/core/execution-operations.d.ts.map +1 -1
  11. package/dist/core/execution-operations.js +60 -40
  12. package/dist/core/execution-operations.js.map +1 -1
  13. package/dist/core/in-process-session.d.ts +96 -0
  14. package/dist/core/in-process-session.d.ts.map +1 -0
  15. package/dist/core/in-process-session.js +175 -0
  16. package/dist/core/in-process-session.js.map +1 -0
  17. package/dist/core/index.d.ts +1 -0
  18. package/dist/core/index.d.ts.map +1 -1
  19. package/dist/core/index.js +1 -0
  20. package/dist/core/index.js.map +1 -1
  21. package/dist/core/provenance-registry.d.ts +3 -0
  22. package/dist/core/provenance-registry.d.ts.map +1 -1
  23. package/dist/core/provenance-registry.js +12 -6
  24. package/dist/core/provenance-registry.js.map +1 -1
  25. package/dist/core/session.d.ts +24 -1
  26. package/dist/core/session.d.ts.map +1 -1
  27. package/dist/core/session.js.map +1 -1
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -0
  31. package/dist/index.js.map +1 -1
  32. package/package.json +8 -5
  33. package/src/client.ts +47 -7
  34. package/src/core/api-operations.ts +35 -3
  35. package/src/core/execution-operations.ts +69 -45
  36. package/src/core/in-process-session.ts +294 -0
  37. package/src/core/index.ts +1 -0
  38. package/src/core/provenance-registry.ts +14 -6
  39. package/src/core/session.ts +20 -1
  40. package/src/index.ts +2 -0
@@ -1,19 +1,22 @@
1
1
  import type { ExecutionResult, ExecutionConfig } from '@mondaydotcomorg/atp-protocol';
2
2
  import { ExecutionStatus, CallbackType } from '@mondaydotcomorg/atp-protocol';
3
3
  import { log } from '@mondaydotcomorg/atp-runtime';
4
- import type { ClientSession } from './session.js';
4
+ import type { ISession } from './session.js';
5
+ import type { InProcessSession } from './in-process-session.js';
5
6
  import type { ServiceProviders } from './service-providers';
6
7
  import { ClientCallbackError } from '../errors.js';
7
8
  import { ProvenanceTokenRegistry } from './provenance-registry.js';
8
9
 
9
10
  export class ExecutionOperations {
10
- private session: ClientSession;
11
+ private session: ISession;
12
+ private inProcessSession?: InProcessSession;
11
13
  private serviceProviders: ServiceProviders;
12
14
  private tokenRegistry: ProvenanceTokenRegistry;
13
15
  private lastExecutionConfig: Partial<ExecutionConfig> | null = null;
14
16
 
15
- constructor(session: ClientSession, serviceProviders: ServiceProviders) {
17
+ constructor(session: ISession, serviceProviders: ServiceProviders, inProcessSession?: InProcessSession) {
16
18
  this.session = session;
19
+ this.inProcessSession = inProcessSession;
17
20
  this.serviceProviders = serviceProviders;
18
21
  this.tokenRegistry = new ProvenanceTokenRegistry();
19
22
  }
@@ -132,24 +135,30 @@ export class ExecutionOperations {
132
135
 
133
136
  this.lastExecutionConfig = executionConfig;
134
137
 
135
- const url = `${this.session.getBaseUrl()}/api/execute`;
136
- const body = JSON.stringify({ code, config: executionConfig });
137
- const headers = await this.session.prepareHeaders('POST', url, body);
138
+ let result: ExecutionResult;
138
139
 
139
- const response = await fetch(url, {
140
- method: 'POST',
141
- headers,
142
- body,
143
- });
140
+ if (this.inProcessSession) {
141
+ result = (await this.inProcessSession.execute(code, executionConfig)) as ExecutionResult;
142
+ } else {
143
+ const url = `${this.session.getBaseUrl()}/api/execute`;
144
+ const body = JSON.stringify({ code, config: executionConfig });
145
+ const headers = await this.session.prepareHeaders('POST', url, body);
144
146
 
145
- this.session.updateToken(response);
147
+ const response = await fetch(url, {
148
+ method: 'POST',
149
+ headers,
150
+ body,
151
+ });
146
152
 
147
- if (!response.ok) {
148
- const error = (await response.json()) as { error: string };
149
- throw new Error(`Execution failed: ${error.error || response.statusText}`);
150
- }
153
+ this.session.updateToken(response);
151
154
 
152
- const result = (await response.json()) as ExecutionResult;
155
+ if (!response.ok) {
156
+ const error = (await response.json()) as { error: string };
157
+ throw new Error(`Execution failed: ${error.error || response.statusText}`);
158
+ }
159
+
160
+ result = (await response.json()) as ExecutionResult;
161
+ }
153
162
 
154
163
  if (result.provenanceTokens && result.provenanceTokens.length > 0) {
155
164
  for (const { token } of result.provenanceTokens) {
@@ -383,24 +392,30 @@ export class ExecutionOperations {
383
392
  async resume(executionId: string, callbackResult: unknown): Promise<ExecutionResult> {
384
393
  await this.session.ensureInitialized();
385
394
 
386
- const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
387
- const body = JSON.stringify({ result: callbackResult });
388
- const headers = await this.session.prepareHeaders('POST', url, body);
395
+ let result: ExecutionResult;
389
396
 
390
- const response = await fetch(url, {
391
- method: 'POST',
392
- headers,
393
- body,
394
- });
397
+ if (this.inProcessSession) {
398
+ result = (await this.inProcessSession.resume(executionId, callbackResult)) as ExecutionResult;
399
+ } else {
400
+ const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
401
+ const body = JSON.stringify({ result: callbackResult });
402
+ const headers = await this.session.prepareHeaders('POST', url, body);
403
+
404
+ const response = await fetch(url, {
405
+ method: 'POST',
406
+ headers,
407
+ body,
408
+ });
395
409
 
396
- this.session.updateToken(response);
410
+ this.session.updateToken(response);
397
411
 
398
- if (!response.ok) {
399
- const error = (await response.json()) as { error: string };
400
- throw new Error(`Resume failed: ${error.error || response.statusText}`);
401
- }
412
+ if (!response.ok) {
413
+ const error = (await response.json()) as { error: string };
414
+ throw new Error(`Resume failed: ${error.error || response.statusText}`);
415
+ }
402
416
 
403
- const result = (await response.json()) as ExecutionResult;
417
+ result = (await response.json()) as ExecutionResult;
418
+ }
404
419
 
405
420
  if (result.provenanceTokens && result.provenanceTokens.length > 0) {
406
421
  for (const { token } of result.provenanceTokens) {
@@ -428,24 +443,33 @@ export class ExecutionOperations {
428
443
  ): Promise<ExecutionResult> {
429
444
  await this.session.ensureInitialized();
430
445
 
431
- const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
432
- const body = JSON.stringify({ results: batchResults });
433
- const headers = await this.session.prepareHeaders('POST', url, body);
446
+ let result: ExecutionResult;
434
447
 
435
- const response = await fetch(url, {
436
- method: 'POST',
437
- headers,
438
- body,
439
- });
448
+ if (this.inProcessSession) {
449
+ result = (await this.inProcessSession.resumeWithBatchResults(
450
+ executionId,
451
+ batchResults
452
+ )) as ExecutionResult;
453
+ } else {
454
+ const url = `${this.session.getBaseUrl()}/api/resume/${executionId}`;
455
+ const body = JSON.stringify({ results: batchResults });
456
+ const headers = await this.session.prepareHeaders('POST', url, body);
440
457
 
441
- this.session.updateToken(response);
458
+ const response = await fetch(url, {
459
+ method: 'POST',
460
+ headers,
461
+ body,
462
+ });
442
463
 
443
- if (!response.ok) {
444
- const error = (await response.json()) as { error: string };
445
- throw new Error(`Batch resume failed: ${error.error || response.statusText}`);
446
- }
464
+ this.session.updateToken(response);
447
465
 
448
- const result = (await response.json()) as ExecutionResult;
466
+ if (!response.ok) {
467
+ const error = (await response.json()) as { error: string };
468
+ throw new Error(`Batch resume failed: ${error.error || response.statusText}`);
469
+ }
470
+
471
+ result = (await response.json()) as ExecutionResult;
472
+ }
449
473
 
450
474
  if (result.provenanceTokens && result.provenanceTokens.length > 0) {
451
475
  for (const { token } of result.provenanceTokens) {
@@ -0,0 +1,294 @@
1
+ import type { ClientToolDefinition } from '@mondaydotcomorg/atp-protocol';
2
+ import type { ISession } from './session.js';
3
+
4
+ interface InProcessServer {
5
+ start(): Promise<void>;
6
+ handleInit(ctx: InProcessRequestContext): Promise<unknown>;
7
+ getDefinitions(ctx?: InProcessRequestContext): Promise<unknown>;
8
+ getRuntimeDefinitions(ctx?: InProcessRequestContext): Promise<string>;
9
+ getInfo(): unknown;
10
+ handleSearch(ctx: InProcessRequestContext): Promise<unknown>;
11
+ handleExplore(ctx: InProcessRequestContext): Promise<unknown>;
12
+ handleExecute(ctx: InProcessRequestContext): Promise<unknown>;
13
+ handleResume(ctx: InProcessRequestContext, executionId: string): Promise<unknown>;
14
+ }
15
+
16
+ interface InProcessRequestContext {
17
+ method: string;
18
+ path: string;
19
+ query: Record<string, string>;
20
+ headers: Record<string, string>;
21
+ body: unknown;
22
+ clientId?: string;
23
+ clientToken?: string;
24
+ userId?: string;
25
+ user?: unknown;
26
+ executionId?: string;
27
+ code?: string;
28
+ validation?: unknown;
29
+ result?: unknown;
30
+ error?: Error;
31
+ logger: { debug: () => void; info: () => void; warn: () => void; error: () => void };
32
+ status: number;
33
+ responseBody: unknown;
34
+ throw(status: number, message: string): never;
35
+ assert(condition: boolean, message: string): asserts condition;
36
+ set(header: string, value: string): void;
37
+ }
38
+
39
+ export class InProcessSession implements ISession {
40
+ private server: InProcessServer;
41
+ private clientId?: string;
42
+ private clientToken?: string;
43
+ private initialized: boolean = false;
44
+ private initPromise?: Promise<void>;
45
+
46
+ constructor(server: InProcessServer) {
47
+ this.server = server;
48
+ }
49
+
50
+ async init(
51
+ clientInfo?: { name?: string; version?: string; [key: string]: unknown },
52
+ tools?: ClientToolDefinition[],
53
+ services?: { hasLLM: boolean; hasApproval: boolean; hasEmbedding: boolean; hasTools: boolean }
54
+ ): Promise<{
55
+ clientId: string;
56
+ token: string;
57
+ expiresAt: number;
58
+ tokenRotateAt: number;
59
+ }> {
60
+ if (this.initPromise) {
61
+ await this.initPromise;
62
+ return {
63
+ clientId: this.clientId!,
64
+ token: this.clientToken!,
65
+ expiresAt: 0,
66
+ tokenRotateAt: 0,
67
+ };
68
+ }
69
+
70
+ this.initPromise = (async () => {
71
+ await this.server.start();
72
+
73
+ const ctx = this.createContext({
74
+ method: 'POST',
75
+ path: '/api/init',
76
+ body: {
77
+ clientInfo,
78
+ tools: tools || [],
79
+ services,
80
+ },
81
+ });
82
+
83
+ const result = (await this.server.handleInit(ctx)) as {
84
+ clientId: string;
85
+ token: string;
86
+ expiresAt: number;
87
+ tokenRotateAt: number;
88
+ };
89
+
90
+ this.clientId = result.clientId;
91
+ this.clientToken = result.token;
92
+ this.initialized = true;
93
+ })();
94
+
95
+ await this.initPromise;
96
+
97
+ return {
98
+ clientId: this.clientId!,
99
+ token: this.clientToken!,
100
+ expiresAt: 0,
101
+ tokenRotateAt: 0,
102
+ };
103
+ }
104
+
105
+ getClientId(): string {
106
+ if (!this.clientId) {
107
+ throw new Error('Client not initialized. Call init() first.');
108
+ }
109
+ return this.clientId;
110
+ }
111
+
112
+ async ensureInitialized(): Promise<void> {
113
+ if (!this.initialized) {
114
+ throw new Error('Client not initialized. Call init() first.');
115
+ }
116
+ }
117
+
118
+ getHeaders(): Record<string, string> {
119
+ const headers: Record<string, string> = {
120
+ 'content-type': 'application/json',
121
+ };
122
+
123
+ if (this.clientId) {
124
+ headers['x-client-id'] = this.clientId;
125
+ }
126
+
127
+ if (this.clientToken) {
128
+ headers['authorization'] = `Bearer ${this.clientToken}`;
129
+ }
130
+
131
+ return headers;
132
+ }
133
+
134
+ getBaseUrl(): string {
135
+ return '';
136
+ }
137
+
138
+ updateToken(_response: Response): void {
139
+ // No-op for in-process - tokens are managed directly
140
+ }
141
+
142
+ async prepareHeaders(
143
+ _method: string,
144
+ _url: string,
145
+ _body?: unknown
146
+ ): Promise<Record<string, string>> {
147
+ return this.getHeaders();
148
+ }
149
+
150
+ async getDefinitions(options?: { apiGroups?: string[] }): Promise<{
151
+ typescript: string;
152
+ version: string;
153
+ apiGroups: string[];
154
+ }> {
155
+ await this.ensureInitialized();
156
+
157
+ const ctx = this.createContext({
158
+ method: 'GET',
159
+ path: '/api/definitions',
160
+ query: options?.apiGroups ? { apiGroups: options.apiGroups.join(',') } : {},
161
+ });
162
+
163
+ return (await this.server.getDefinitions(ctx)) as {
164
+ typescript: string;
165
+ version: string;
166
+ apiGroups: string[];
167
+ };
168
+ }
169
+
170
+ async getRuntimeDefinitions(options?: { apis?: string[] }): Promise<string> {
171
+ await this.ensureInitialized();
172
+
173
+ const ctx = this.createContext({
174
+ method: 'GET',
175
+ path: '/api/runtime',
176
+ query: options?.apis?.length ? { apis: options.apis.join(',') } : {},
177
+ });
178
+
179
+ return await this.server.getRuntimeDefinitions(ctx);
180
+ }
181
+
182
+ async getServerInfo(): Promise<{
183
+ version: string;
184
+ capabilities: Record<string, boolean>;
185
+ }> {
186
+ await this.ensureInitialized();
187
+ return this.server.getInfo() as {
188
+ version: string;
189
+ capabilities: Record<string, boolean>;
190
+ };
191
+ }
192
+
193
+ async search(query: string, options?: Record<string, unknown>): Promise<{ results: unknown[] }> {
194
+ await this.ensureInitialized();
195
+
196
+ const ctx = this.createContext({
197
+ method: 'POST',
198
+ path: '/api/search',
199
+ body: { query, ...options },
200
+ });
201
+
202
+ return (await this.server.handleSearch(ctx)) as { results: unknown[] };
203
+ }
204
+
205
+ async explore(path: string): Promise<unknown> {
206
+ await this.ensureInitialized();
207
+
208
+ const ctx = this.createContext({
209
+ method: 'POST',
210
+ path: '/api/explore',
211
+ body: { path },
212
+ });
213
+
214
+ return await this.server.handleExplore(ctx);
215
+ }
216
+
217
+ async execute(code: string, config?: Record<string, unknown>): Promise<unknown> {
218
+ await this.ensureInitialized();
219
+
220
+ const ctx = this.createContext({
221
+ method: 'POST',
222
+ path: '/api/execute',
223
+ body: { code, config },
224
+ });
225
+
226
+ return await this.server.handleExecute(ctx);
227
+ }
228
+
229
+ async resume(executionId: string, callbackResult: unknown): Promise<unknown> {
230
+ await this.ensureInitialized();
231
+
232
+ const ctx = this.createContext({
233
+ method: 'POST',
234
+ path: `/api/resume/${executionId}`,
235
+ body: { result: callbackResult },
236
+ });
237
+
238
+ return await this.server.handleResume(ctx, executionId);
239
+ }
240
+
241
+ async resumeWithBatchResults(
242
+ executionId: string,
243
+ batchResults: Array<{ id: string; result: unknown }>
244
+ ): Promise<unknown> {
245
+ await this.ensureInitialized();
246
+
247
+ const ctx = this.createContext({
248
+ method: 'POST',
249
+ path: `/api/resume/${executionId}`,
250
+ body: { results: batchResults },
251
+ });
252
+
253
+ return await this.server.handleResume(ctx, executionId);
254
+ }
255
+
256
+ private createContext(options: {
257
+ method: string;
258
+ path: string;
259
+ query?: Record<string, string>;
260
+ body?: unknown;
261
+ }): InProcessRequestContext {
262
+ const noopLogger = {
263
+ debug: () => {},
264
+ info: () => {},
265
+ warn: () => {},
266
+ error: () => {},
267
+ };
268
+
269
+ return {
270
+ method: options.method,
271
+ path: options.path,
272
+ query: options.query || {},
273
+ headers: this.getHeaders(),
274
+ body: options.body,
275
+ clientId: this.clientId,
276
+ clientToken: this.clientToken,
277
+ logger: noopLogger,
278
+ status: 200,
279
+ responseBody: null,
280
+ throw: (status: number, message: string) => {
281
+ const error = new Error(message);
282
+ (error as Error & { status: number }).status = status;
283
+ throw error;
284
+ },
285
+ assert: (condition: boolean, message: string) => {
286
+ if (!condition) {
287
+ throw new Error(message);
288
+ }
289
+ },
290
+ set: () => {},
291
+ };
292
+ }
293
+ }
294
+
package/src/core/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './session.js';
2
+ export * from './in-process-session.js';
2
3
  export * from './api-operations.js';
3
4
  export * from './execution-operations.js';
4
5
  export * from './service-providers.js';
@@ -7,12 +7,14 @@
7
7
  export interface TokenEntry {
8
8
  token: string;
9
9
  addedAt: number;
10
+ sequence: number;
10
11
  }
11
12
 
12
13
  export class ProvenanceTokenRegistry {
13
14
  private cache: Map<string, TokenEntry> = new Map();
14
15
  private maxSize: number;
15
16
  private ttl: number;
17
+ private sequenceCounter: number = 0;
16
18
 
17
19
  constructor(maxSize: number = 10000, ttlHours: number = 1) {
18
20
  this.maxSize = maxSize;
@@ -31,17 +33,23 @@ export class ProvenanceTokenRegistry {
31
33
  this.evictLRU();
32
34
  }
33
35
 
34
- // Store token
36
+ // Store token with sequence number for stable ordering
35
37
  this.cache.set(token, {
36
38
  token,
37
39
  addedAt: Date.now(),
40
+ sequence: this.sequenceCounter++,
38
41
  });
39
42
  }
40
43
 
41
44
  /**
42
45
  * Get recent tokens (non-expired, sorted by age, limited)
46
+ * Returns tokens in chronological order (oldest first, most recent last)
43
47
  */
44
48
  getRecentTokens(maxCount: number = 1000): string[] {
49
+ if (maxCount <= 0) {
50
+ return [];
51
+ }
52
+
45
53
  this.evictExpired();
46
54
 
47
55
  const now = Date.now();
@@ -66,8 +74,8 @@ export class ProvenanceTokenRegistry {
66
74
  return false;
67
75
  }
68
76
  })
69
- .sort((a, b) => b.addedAt - a.addedAt)
70
- .slice(0, maxCount);
77
+ .sort((a, b) => a.sequence - b.sequence)
78
+ .slice(-maxCount);
71
79
 
72
80
  for (const token of expiredTokens) {
73
81
  this.cache.delete(token);
@@ -113,11 +121,11 @@ export class ProvenanceTokenRegistry {
113
121
  */
114
122
  private evictLRU(): void {
115
123
  let oldestToken: string | null = null;
116
- let oldestTime = Infinity;
124
+ let oldestSequence = Infinity;
117
125
 
118
126
  for (const [token, entry] of this.cache.entries()) {
119
- if (entry.addedAt < oldestTime) {
120
- oldestTime = entry.addedAt;
127
+ if (entry.sequence < oldestSequence) {
128
+ oldestSequence = entry.sequence;
121
129
  oldestToken = token;
122
130
  }
123
131
  }
@@ -1,7 +1,26 @@
1
1
  import type { ClientHooks } from './types.js';
2
2
  import type { ClientToolDefinition } from '@mondaydotcomorg/atp-protocol';
3
3
 
4
- export class ClientSession {
4
+ export interface ISession {
5
+ init(
6
+ clientInfo?: { name?: string; version?: string; [key: string]: unknown },
7
+ tools?: ClientToolDefinition[],
8
+ services?: { hasLLM: boolean; hasApproval: boolean; hasEmbedding: boolean; hasTools: boolean }
9
+ ): Promise<{
10
+ clientId: string;
11
+ token: string;
12
+ expiresAt: number;
13
+ tokenRotateAt: number;
14
+ }>;
15
+ getClientId(): string;
16
+ ensureInitialized(): Promise<void>;
17
+ getHeaders(): Record<string, string>;
18
+ getBaseUrl(): string;
19
+ updateToken(response: Response): void;
20
+ prepareHeaders(method: string, url: string, body?: unknown): Promise<Record<string, string>>;
21
+ }
22
+
23
+ export class ClientSession implements ISession {
5
24
  private baseUrl: string;
6
25
  private customHeaders: Record<string, string>;
7
26
  private clientId?: string;
package/src/index.ts CHANGED
@@ -9,3 +9,5 @@ export type { RuntimeAPIName } from '@mondaydotcomorg/atp-runtime';
9
9
  export type { Tool, ToolName } from './tools/types.js';
10
10
  export { ToolNames } from './tools/types.js';
11
11
  export type { AgentToolProtocolClientOptions } from './client.js';
12
+ export { InProcessSession } from './core/in-process-session.js';
13
+ export type { ISession } from './core/session.js';