@agentuity/core 0.1.16 → 0.1.18
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.
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/services/keyvalue.d.ts +94 -6
- package/dist/services/keyvalue.d.ts.map +1 -1
- package/dist/services/keyvalue.js +56 -5
- package/dist/services/keyvalue.js.map +1 -1
- package/dist/services/queue.d.ts +257 -0
- package/dist/services/queue.d.ts.map +1 -0
- package/dist/services/queue.js +207 -0
- package/dist/services/queue.js.map +1 -0
- package/dist/services/sandbox.d.ts +8 -0
- package/dist/services/sandbox.d.ts.map +1 -1
- package/dist/services/sandbox.js.map +1 -1
- package/dist/services/vector.d.ts +53 -12
- package/dist/services/vector.d.ts.map +1 -1
- package/dist/services/vector.js +24 -4
- package/dist/services/vector.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +17 -0
- package/src/services/keyvalue.ts +130 -10
- package/src/services/queue.ts +384 -0
- package/src/services/sandbox.ts +10 -0
- package/src/services/vector.ts +79 -18
package/src/services/keyvalue.ts
CHANGED
|
@@ -2,6 +2,21 @@ import { FetchAdapter } from './adapter';
|
|
|
2
2
|
import { buildUrl, toServiceException, toPayload } from './_util';
|
|
3
3
|
import { StructuredError } from '../error';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Minimum TTL value in seconds (1 minute)
|
|
7
|
+
*/
|
|
8
|
+
export const KV_MIN_TTL_SECONDS = 60;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Maximum TTL value in seconds (90 days)
|
|
12
|
+
*/
|
|
13
|
+
export const KV_MAX_TTL_SECONDS = 7776000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Default TTL value in seconds (7 days) - used when namespace is auto-created or no TTL specified
|
|
17
|
+
*/
|
|
18
|
+
export const KV_DEFAULT_TTL_SECONDS = 604800;
|
|
19
|
+
|
|
5
20
|
/**
|
|
6
21
|
* the result of a data operation when the data is found
|
|
7
22
|
*/
|
|
@@ -49,6 +64,23 @@ export interface KeyValueStorageSetParams {
|
|
|
49
64
|
contentType?: string;
|
|
50
65
|
}
|
|
51
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Parameters for creating a namespace
|
|
69
|
+
*/
|
|
70
|
+
export interface CreateNamespaceParams {
|
|
71
|
+
/**
|
|
72
|
+
* Default TTL for keys in this namespace (in seconds).
|
|
73
|
+
* - If undefined/omitted: uses server default (7 days / 604,800 seconds)
|
|
74
|
+
* - If 0: keys will not expire by default
|
|
75
|
+
* - If 60-7,776,000: custom TTL in seconds (1 minute to 90 days)
|
|
76
|
+
*
|
|
77
|
+
* Keys can override this default by specifying TTL in the set() call.
|
|
78
|
+
* Active keys are automatically extended (sliding expiration) when read
|
|
79
|
+
* if their remaining TTL is less than 50% of the original TTL.
|
|
80
|
+
*/
|
|
81
|
+
defaultTTLSeconds?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
52
84
|
/**
|
|
53
85
|
* Statistics for a key-value store namespace
|
|
54
86
|
*/
|
|
@@ -70,6 +102,46 @@ export interface KeyValueItemWithMetadata<T = unknown> {
|
|
|
70
102
|
updated_at: string;
|
|
71
103
|
}
|
|
72
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Parameters for getting all namespace statistics with optional pagination
|
|
107
|
+
*/
|
|
108
|
+
export interface GetAllStatsParams {
|
|
109
|
+
/**
|
|
110
|
+
* Maximum number of namespaces to return (default: 100, max: 1000)
|
|
111
|
+
*/
|
|
112
|
+
limit?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Number of namespaces to skip for pagination (default: 0)
|
|
115
|
+
*/
|
|
116
|
+
offset?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Paginated response for namespace statistics
|
|
121
|
+
*/
|
|
122
|
+
export interface KeyValueStatsPaginated {
|
|
123
|
+
/**
|
|
124
|
+
* Map of namespace names to their statistics
|
|
125
|
+
*/
|
|
126
|
+
namespaces: Record<string, KeyValueStats>;
|
|
127
|
+
/**
|
|
128
|
+
* Total number of namespaces across all pages
|
|
129
|
+
*/
|
|
130
|
+
total: number;
|
|
131
|
+
/**
|
|
132
|
+
* Number of namespaces requested per page
|
|
133
|
+
*/
|
|
134
|
+
limit: number;
|
|
135
|
+
/**
|
|
136
|
+
* Number of namespaces skipped
|
|
137
|
+
*/
|
|
138
|
+
offset: number;
|
|
139
|
+
/**
|
|
140
|
+
* Whether there are more namespaces available
|
|
141
|
+
*/
|
|
142
|
+
hasMore: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
73
145
|
export interface KeyValueStorage {
|
|
74
146
|
/**
|
|
75
147
|
* get a value from the key value storage
|
|
@@ -114,14 +186,20 @@ export interface KeyValueStorage {
|
|
|
114
186
|
/**
|
|
115
187
|
* get statistics for all namespaces
|
|
116
188
|
*
|
|
117
|
-
* @
|
|
189
|
+
* @param params - optional pagination parameters
|
|
190
|
+
* @returns map of namespace names to statistics, or paginated response if params provided
|
|
118
191
|
*/
|
|
119
|
-
getAllStats(): Promise<Record<string, KeyValueStats
|
|
192
|
+
getAllStats(params?: GetAllStatsParams): Promise<Record<string, KeyValueStats> | KeyValueStatsPaginated>;
|
|
120
193
|
|
|
121
194
|
/**
|
|
122
195
|
* get all namespace names
|
|
123
196
|
*
|
|
124
|
-
* @returns array of namespace names
|
|
197
|
+
* @returns array of namespace names (up to 1000)
|
|
198
|
+
*
|
|
199
|
+
* @remarks
|
|
200
|
+
* This method returns a maximum of 1000 namespace names.
|
|
201
|
+
* If you have more than 1000 namespaces, only the first 1000
|
|
202
|
+
* (ordered by creation date, most recent first) will be returned.
|
|
125
203
|
*/
|
|
126
204
|
getNamespaces(): Promise<string[]>;
|
|
127
205
|
|
|
@@ -156,8 +234,9 @@ export interface KeyValueStorage {
|
|
|
156
234
|
* create a new namespace
|
|
157
235
|
*
|
|
158
236
|
* @param name - the name of the key value storage to create
|
|
237
|
+
* @param params - optional parameters including default TTL
|
|
159
238
|
*/
|
|
160
|
-
createNamespace(name: string): Promise<void>;
|
|
239
|
+
createNamespace(name: string, params?: CreateNamespaceParams): Promise<void>;
|
|
161
240
|
}
|
|
162
241
|
|
|
163
242
|
const KeyValueInvalidTTLError = StructuredError('KeyValueInvalidTTLError');
|
|
@@ -201,6 +280,21 @@ export class KeyValueStorageService implements KeyValueStorage {
|
|
|
201
280
|
throw await toServiceException('GET', url, res.response);
|
|
202
281
|
}
|
|
203
282
|
|
|
283
|
+
/**
|
|
284
|
+
* set a value in the key value storage
|
|
285
|
+
*
|
|
286
|
+
* @param name - the name of the key value storage
|
|
287
|
+
* @param key - the key to set the value of
|
|
288
|
+
* @param value - the value to set in any of the supported data types
|
|
289
|
+
* @param params - the KeyValueStorageSetParams
|
|
290
|
+
*
|
|
291
|
+
* @remarks
|
|
292
|
+
* TTL behavior:
|
|
293
|
+
* - If TTL is not specified, the key inherits the namespace's default TTL
|
|
294
|
+
* - TTL values below 60 seconds are clamped to 60 seconds
|
|
295
|
+
* - TTL values above 7,776,000 seconds (90 days) are clamped to 90 days
|
|
296
|
+
* - If the namespace doesn't exist, it is auto-created with a 7-day default TTL
|
|
297
|
+
*/
|
|
204
298
|
async set<T = unknown>(
|
|
205
299
|
name: string,
|
|
206
300
|
key: string,
|
|
@@ -282,10 +376,18 @@ export class KeyValueStorageService implements KeyValueStorage {
|
|
|
282
376
|
throw await toServiceException('GET', url, res.response);
|
|
283
377
|
}
|
|
284
378
|
|
|
285
|
-
async getAllStats(): Promise<Record<string, KeyValueStats
|
|
286
|
-
const
|
|
379
|
+
async getAllStats(params?: GetAllStatsParams): Promise<Record<string, KeyValueStats> | KeyValueStatsPaginated> {
|
|
380
|
+
const queryParams = new URLSearchParams();
|
|
381
|
+
if (params?.limit !== undefined) {
|
|
382
|
+
queryParams.set('limit', String(params.limit));
|
|
383
|
+
}
|
|
384
|
+
if (params?.offset !== undefined) {
|
|
385
|
+
queryParams.set('offset', String(params.offset));
|
|
386
|
+
}
|
|
387
|
+
const queryString = queryParams.toString();
|
|
388
|
+
const url = buildUrl(this.#baseUrl, `/kv/2025-03-17/stats${queryString ? `?${queryString}` : ''}`);
|
|
287
389
|
const signal = AbortSignal.timeout(10_000);
|
|
288
|
-
const res = await this.#adapter.invoke<Record<string, KeyValueStats
|
|
390
|
+
const res = await this.#adapter.invoke<Record<string, KeyValueStats> | KeyValueStatsPaginated>(url, {
|
|
289
391
|
method: 'GET',
|
|
290
392
|
signal,
|
|
291
393
|
telemetry: {
|
|
@@ -300,8 +402,20 @@ export class KeyValueStorageService implements KeyValueStorage {
|
|
|
300
402
|
}
|
|
301
403
|
|
|
302
404
|
async getNamespaces(): Promise<string[]> {
|
|
303
|
-
const
|
|
304
|
-
|
|
405
|
+
const url = buildUrl(this.#baseUrl, '/kv/2025-03-17/namespaces');
|
|
406
|
+
const signal = AbortSignal.timeout(10_000);
|
|
407
|
+
const res = await this.#adapter.invoke<string[]>(url, {
|
|
408
|
+
method: 'GET',
|
|
409
|
+
signal,
|
|
410
|
+
telemetry: {
|
|
411
|
+
name: 'agentuity.keyvalue.getNamespaces',
|
|
412
|
+
attributes: {},
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
if (res.ok) {
|
|
416
|
+
return res.data;
|
|
417
|
+
}
|
|
418
|
+
throw await toServiceException('GET', url, res.response);
|
|
305
419
|
}
|
|
306
420
|
|
|
307
421
|
async search<T = unknown>(
|
|
@@ -361,12 +475,18 @@ export class KeyValueStorageService implements KeyValueStorage {
|
|
|
361
475
|
throw await toServiceException('DELETE', url, res.response);
|
|
362
476
|
}
|
|
363
477
|
|
|
364
|
-
async createNamespace(name: string): Promise<void> {
|
|
478
|
+
async createNamespace(name: string, params?: CreateNamespaceParams): Promise<void> {
|
|
365
479
|
const url = buildUrl(this.#baseUrl, `/kv/2025-03-17/${encodeURIComponent(name)}`);
|
|
366
480
|
const signal = AbortSignal.timeout(10_000);
|
|
481
|
+
|
|
482
|
+
const body = params?.defaultTTLSeconds !== undefined
|
|
483
|
+
? JSON.stringify({ default_ttl_seconds: params.defaultTTLSeconds })
|
|
484
|
+
: undefined;
|
|
485
|
+
|
|
367
486
|
const res = await this.#adapter.invoke(url, {
|
|
368
487
|
method: 'POST',
|
|
369
488
|
signal,
|
|
489
|
+
...(body && { body, contentType: 'application/json' }),
|
|
370
490
|
telemetry: {
|
|
371
491
|
name: 'agentuity.keyvalue.createNamespace',
|
|
372
492
|
attributes: { name },
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module queue
|
|
3
|
+
*
|
|
4
|
+
* Queue service for publishing messages to Agentuity queues.
|
|
5
|
+
*
|
|
6
|
+
* This module provides a simplified interface for agents to publish messages
|
|
7
|
+
* to queues. For full queue management (CRUD, consume, acknowledge), use
|
|
8
|
+
* the `@agentuity/server` package.
|
|
9
|
+
*
|
|
10
|
+
* @example Publishing from an agent
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // Inside an agent handler
|
|
13
|
+
* const result = await ctx.queue.publish('order-queue', {
|
|
14
|
+
* orderId: 123,
|
|
15
|
+
* action: 'process',
|
|
16
|
+
* });
|
|
17
|
+
* console.log(`Published message ${result.id}`);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { FetchAdapter } from './adapter';
|
|
22
|
+
import { buildUrl, toServiceException, toPayload } from './_util';
|
|
23
|
+
import { StructuredError } from '../error';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parameters for publishing a message to a queue.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const params: QueuePublishParams = {
|
|
31
|
+
* metadata: { priority: 'high' },
|
|
32
|
+
* partitionKey: 'customer-123',
|
|
33
|
+
* idempotencyKey: 'order-456-v1',
|
|
34
|
+
* ttl: 3600, // 1 hour
|
|
35
|
+
* };
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface QueuePublishParams {
|
|
39
|
+
/**
|
|
40
|
+
* Optional metadata to attach to the message.
|
|
41
|
+
* Can contain any JSON-serializable data for message routing or filtering.
|
|
42
|
+
*/
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Optional partition key for message ordering.
|
|
47
|
+
* Messages with the same partition key are guaranteed to be processed in order.
|
|
48
|
+
*/
|
|
49
|
+
partitionKey?: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Optional idempotency key for deduplication.
|
|
53
|
+
* If a message with the same key was recently published, it will be deduplicated.
|
|
54
|
+
*/
|
|
55
|
+
idempotencyKey?: string;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Optional time-to-live in seconds.
|
|
59
|
+
* Messages will expire and be removed after this duration.
|
|
60
|
+
*/
|
|
61
|
+
ttl?: number;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Optional project ID for cross-project publishing.
|
|
65
|
+
* If not specified, uses the current project context.
|
|
66
|
+
*/
|
|
67
|
+
projectId?: string;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Optional agent ID for attribution.
|
|
71
|
+
* If not specified, uses the current agent context.
|
|
72
|
+
*/
|
|
73
|
+
agentId?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Result of publishing a message to a queue.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const result = await queue.publish('my-queue', payload);
|
|
82
|
+
* console.log(`Message ${result.id} published at offset ${result.offset}`);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export interface QueuePublishResult {
|
|
86
|
+
/**
|
|
87
|
+
* The unique message ID (prefixed with msg_).
|
|
88
|
+
* Use this ID to track, acknowledge, or delete the message.
|
|
89
|
+
*/
|
|
90
|
+
id: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The sequential offset of the message in the queue.
|
|
94
|
+
* Offsets are monotonically increasing and can be used for log-style consumption.
|
|
95
|
+
*/
|
|
96
|
+
offset: number;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* ISO 8601 timestamp when the message was published.
|
|
100
|
+
*/
|
|
101
|
+
publishedAt: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Queue service interface for publishing messages.
|
|
106
|
+
*
|
|
107
|
+
* This is the interface available to agents via `ctx.queue`. It provides
|
|
108
|
+
* a simple publish-only interface suitable for agent workflows.
|
|
109
|
+
*
|
|
110
|
+
* For full queue management (create queues, consume messages, manage destinations),
|
|
111
|
+
* use the `@agentuity/server` package.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* // In an agent handler
|
|
116
|
+
* export default createAgent('my-agent', {
|
|
117
|
+
* handler: async (ctx, input) => {
|
|
118
|
+
* // Publish a message to a queue
|
|
119
|
+
* await ctx.queue.publish('notifications', {
|
|
120
|
+
* type: 'email',
|
|
121
|
+
* to: input.email,
|
|
122
|
+
* subject: 'Welcome!',
|
|
123
|
+
* });
|
|
124
|
+
* return { success: true };
|
|
125
|
+
* },
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export interface QueueService {
|
|
130
|
+
/**
|
|
131
|
+
* Publish a message to a queue.
|
|
132
|
+
*
|
|
133
|
+
* The payload can be a string or an object. Objects are automatically
|
|
134
|
+
* JSON-stringified before publishing.
|
|
135
|
+
*
|
|
136
|
+
* @param queueName - The name of the queue to publish to
|
|
137
|
+
* @param payload - The message payload (string or JSON-serializable object)
|
|
138
|
+
* @param params - Optional publish parameters (metadata, TTL, etc.)
|
|
139
|
+
* @returns The publish result with message ID and offset
|
|
140
|
+
* @throws {QueueNotFoundError} If the queue does not exist
|
|
141
|
+
* @throws {QueueValidationError} If validation fails (invalid name, payload too large, etc.)
|
|
142
|
+
* @throws {QueuePublishError} If the publish operation fails
|
|
143
|
+
*
|
|
144
|
+
* @example Publishing a simple message
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const result = await ctx.queue.publish('my-queue', 'Hello, World!');
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @example Publishing with options
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const result = await ctx.queue.publish('my-queue', { task: 'process' }, {
|
|
152
|
+
* metadata: { priority: 'high' },
|
|
153
|
+
* idempotencyKey: 'task-123',
|
|
154
|
+
* ttl: 3600,
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
publish(
|
|
159
|
+
queueName: string,
|
|
160
|
+
payload: string | object,
|
|
161
|
+
params?: QueuePublishParams
|
|
162
|
+
): Promise<QueuePublishResult>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// Errors
|
|
167
|
+
// ============================================================================
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Error thrown when a publish operation fails.
|
|
171
|
+
*
|
|
172
|
+
* This is a general error for publish failures that aren't specifically
|
|
173
|
+
* validation or not-found errors.
|
|
174
|
+
*/
|
|
175
|
+
export const QueuePublishError = StructuredError('QueuePublishError');
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Error thrown when a queue is not found.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* try {
|
|
183
|
+
* await ctx.queue.publish('non-existent', 'payload');
|
|
184
|
+
* } catch (error) {
|
|
185
|
+
* if (error instanceof QueueNotFoundError) {
|
|
186
|
+
* console.error('Queue does not exist');
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export const QueueNotFoundError = StructuredError('QueueNotFoundError');
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Error thrown when validation fails.
|
|
195
|
+
*
|
|
196
|
+
* Contains the field name and optionally the invalid value for debugging.
|
|
197
|
+
*/
|
|
198
|
+
export const QueueValidationError = StructuredError('QueueValidationError')<{
|
|
199
|
+
/** The field that failed validation */
|
|
200
|
+
field: string;
|
|
201
|
+
/** The invalid value (for debugging) */
|
|
202
|
+
value?: unknown;
|
|
203
|
+
}>();
|
|
204
|
+
|
|
205
|
+
// ============================================================================
|
|
206
|
+
// Internal Validation
|
|
207
|
+
// ============================================================================
|
|
208
|
+
|
|
209
|
+
const MAX_QUEUE_NAME_LENGTH = 256;
|
|
210
|
+
const MAX_PAYLOAD_SIZE = 1048576;
|
|
211
|
+
const MAX_PARTITION_KEY_LENGTH = 256;
|
|
212
|
+
const MAX_IDEMPOTENCY_KEY_LENGTH = 256;
|
|
213
|
+
const VALID_QUEUE_NAME_REGEX = /^[a-z_][a-z0-9_-]*$/;
|
|
214
|
+
|
|
215
|
+
/** @internal */
|
|
216
|
+
function validateQueueNameInternal(name: string): void {
|
|
217
|
+
if (!name || name.length === 0) {
|
|
218
|
+
throw new QueueValidationError({
|
|
219
|
+
message: 'Queue name cannot be empty',
|
|
220
|
+
field: 'queueName',
|
|
221
|
+
value: name,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (name.length > MAX_QUEUE_NAME_LENGTH) {
|
|
225
|
+
throw new QueueValidationError({
|
|
226
|
+
message: `Queue name must not exceed ${MAX_QUEUE_NAME_LENGTH} characters`,
|
|
227
|
+
field: 'queueName',
|
|
228
|
+
value: name,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (!VALID_QUEUE_NAME_REGEX.test(name)) {
|
|
232
|
+
throw new QueueValidationError({
|
|
233
|
+
message:
|
|
234
|
+
'Queue name must start with a letter or underscore and contain only lowercase letters, digits, underscores, and hyphens',
|
|
235
|
+
field: 'queueName',
|
|
236
|
+
value: name,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** @internal */
|
|
242
|
+
function validatePayloadInternal(payload: string): void {
|
|
243
|
+
if (!payload || payload.length === 0) {
|
|
244
|
+
throw new QueueValidationError({
|
|
245
|
+
message: 'Payload cannot be empty',
|
|
246
|
+
field: 'payload',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (payload.length > MAX_PAYLOAD_SIZE) {
|
|
250
|
+
throw new QueueValidationError({
|
|
251
|
+
message: `Payload size exceeds ${MAX_PAYLOAD_SIZE} byte limit (${payload.length} bytes)`,
|
|
252
|
+
field: 'payload',
|
|
253
|
+
value: payload.length,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ============================================================================
|
|
259
|
+
// QueueStorageService Implementation
|
|
260
|
+
// ============================================================================
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* HTTP-based implementation of the QueueService interface.
|
|
264
|
+
*
|
|
265
|
+
* This service communicates with the Agentuity Queue API to publish messages.
|
|
266
|
+
* It is automatically configured and available via `ctx.queue` in agent handlers.
|
|
267
|
+
*
|
|
268
|
+
* @internal This class is instantiated by the runtime; use `ctx.queue` instead.
|
|
269
|
+
*/
|
|
270
|
+
export class QueueStorageService implements QueueService {
|
|
271
|
+
#adapter: FetchAdapter;
|
|
272
|
+
#baseUrl: string;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Creates a new QueueStorageService.
|
|
276
|
+
*
|
|
277
|
+
* @param baseUrl - The base URL of the Queue API
|
|
278
|
+
* @param adapter - The fetch adapter for making HTTP requests
|
|
279
|
+
*/
|
|
280
|
+
constructor(baseUrl: string, adapter: FetchAdapter) {
|
|
281
|
+
this.#adapter = adapter;
|
|
282
|
+
this.#baseUrl = baseUrl;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @inheritdoc
|
|
287
|
+
*/
|
|
288
|
+
async publish(
|
|
289
|
+
queueName: string,
|
|
290
|
+
payload: string | object,
|
|
291
|
+
params?: QueuePublishParams
|
|
292
|
+
): Promise<QueuePublishResult> {
|
|
293
|
+
// Validate inputs before sending to API
|
|
294
|
+
validateQueueNameInternal(queueName);
|
|
295
|
+
|
|
296
|
+
const [body] = await toPayload(payload);
|
|
297
|
+
const payloadStr = typeof payload === 'string' ? payload : (body as string);
|
|
298
|
+
validatePayloadInternal(payloadStr);
|
|
299
|
+
|
|
300
|
+
// Validate optional params
|
|
301
|
+
if (params?.partitionKey && params.partitionKey.length > MAX_PARTITION_KEY_LENGTH) {
|
|
302
|
+
throw new QueueValidationError({
|
|
303
|
+
message: `Partition key must not exceed ${MAX_PARTITION_KEY_LENGTH} characters`,
|
|
304
|
+
field: 'partitionKey',
|
|
305
|
+
value: params.partitionKey.length,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (params?.idempotencyKey && params.idempotencyKey.length > MAX_IDEMPOTENCY_KEY_LENGTH) {
|
|
309
|
+
throw new QueueValidationError({
|
|
310
|
+
message: `Idempotency key must not exceed ${MAX_IDEMPOTENCY_KEY_LENGTH} characters`,
|
|
311
|
+
field: 'idempotencyKey',
|
|
312
|
+
value: params.idempotencyKey.length,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (params?.ttl !== undefined && params.ttl < 0) {
|
|
316
|
+
throw new QueueValidationError({
|
|
317
|
+
message: 'TTL cannot be negative',
|
|
318
|
+
field: 'ttl',
|
|
319
|
+
value: params.ttl,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const url = buildUrl(
|
|
324
|
+
this.#baseUrl,
|
|
325
|
+
`/queue/messages/publish/2026-01-15/${encodeURIComponent(queueName)}`
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const requestBody: Record<string, unknown> = {
|
|
329
|
+
payload: typeof payload === 'string' ? payload : body,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (params?.metadata) {
|
|
333
|
+
requestBody.metadata = params.metadata;
|
|
334
|
+
}
|
|
335
|
+
if (params?.partitionKey) {
|
|
336
|
+
requestBody.partition_key = params.partitionKey;
|
|
337
|
+
}
|
|
338
|
+
if (params?.idempotencyKey) {
|
|
339
|
+
requestBody.idempotency_key = params.idempotencyKey;
|
|
340
|
+
}
|
|
341
|
+
if (params?.ttl !== undefined) {
|
|
342
|
+
requestBody.ttl_seconds = params.ttl;
|
|
343
|
+
}
|
|
344
|
+
if (params?.projectId) {
|
|
345
|
+
requestBody.project_id = params.projectId;
|
|
346
|
+
}
|
|
347
|
+
if (params?.agentId) {
|
|
348
|
+
requestBody.agent_id = params.agentId;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const signal = AbortSignal.timeout(30_000);
|
|
352
|
+
const res = await this.#adapter.invoke<QueuePublishResult>(url, {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
signal,
|
|
355
|
+
body: JSON.stringify(requestBody),
|
|
356
|
+
contentType: 'application/json',
|
|
357
|
+
telemetry: {
|
|
358
|
+
name: 'agentuity.queue.publish',
|
|
359
|
+
attributes: {
|
|
360
|
+
queueName,
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
if (res.ok) {
|
|
366
|
+
const data = res.data as unknown as {
|
|
367
|
+
message: { id: string; offset: number; published_at: string };
|
|
368
|
+
};
|
|
369
|
+
return {
|
|
370
|
+
id: data.message.id,
|
|
371
|
+
offset: data.message.offset,
|
|
372
|
+
publishedAt: data.message.published_at,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (res.response.status === 404) {
|
|
377
|
+
throw new QueueNotFoundError({
|
|
378
|
+
message: `Queue not found: ${queueName}`,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
throw await toServiceException('POST', url, res.response);
|
|
383
|
+
}
|
|
384
|
+
}
|
package/src/services/sandbox.ts
CHANGED
|
@@ -507,6 +507,11 @@ export interface SandboxInfo {
|
|
|
507
507
|
*/
|
|
508
508
|
runtimeIconUrl?: string;
|
|
509
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Runtime brand color (hex color code)
|
|
512
|
+
*/
|
|
513
|
+
runtimeBrandColor?: string;
|
|
514
|
+
|
|
510
515
|
/**
|
|
511
516
|
* Snapshot ID this sandbox was created from
|
|
512
517
|
*/
|
|
@@ -522,6 +527,11 @@ export interface SandboxInfo {
|
|
|
522
527
|
*/
|
|
523
528
|
executions: number;
|
|
524
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Exit code from the last execution (only available for terminated/failed sandboxes)
|
|
532
|
+
*/
|
|
533
|
+
exitCode?: number;
|
|
534
|
+
|
|
525
535
|
/**
|
|
526
536
|
* URL to the stdout output stream
|
|
527
537
|
*/
|