@agentuity/core 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ import { FetchAdapter } from './adapter.ts';
2
+ import { buildUrl, toServiceException } from './_util.ts';
3
+
4
+ export interface Schedule {
5
+ id: string;
6
+ created_at: string;
7
+ updated_at: string;
8
+ created_by: string;
9
+ name: string;
10
+ description: string | null;
11
+ expression: string;
12
+ due_date: string;
13
+ }
14
+
15
+ export interface ScheduleDestination {
16
+ id: string;
17
+ schedule_id: string;
18
+ created_at: string;
19
+ updated_at: string;
20
+ created_by: string;
21
+ type: 'url' | 'sandbox';
22
+ config: Record<string, unknown>;
23
+ }
24
+
25
+ export interface ScheduleDelivery {
26
+ id: string;
27
+ date: string;
28
+ schedule_id: string;
29
+ schedule_destination_id: string;
30
+ status: 'pending' | 'success' | 'failed';
31
+ retries: number;
32
+ error: string | null;
33
+ response: Record<string, unknown> | null;
34
+ }
35
+
36
+ export interface CreateScheduleParams {
37
+ name: string;
38
+ description?: string;
39
+ expression: string;
40
+ destinations?: CreateScheduleDestinationParams[];
41
+ }
42
+
43
+ export interface CreateScheduleDestinationParams {
44
+ type: 'url' | 'sandbox';
45
+ config: Record<string, unknown>;
46
+ }
47
+
48
+ export interface UpdateScheduleParams {
49
+ name?: string;
50
+ description?: string;
51
+ expression?: string;
52
+ }
53
+
54
+ export interface ScheduleListResult {
55
+ schedules: Schedule[];
56
+ total: number;
57
+ }
58
+
59
+ export interface ScheduleGetResult {
60
+ schedule: Schedule;
61
+ destinations: ScheduleDestination[];
62
+ }
63
+
64
+ export interface ScheduleCreateResult {
65
+ schedule: Schedule;
66
+ destinations: ScheduleDestination[];
67
+ }
68
+
69
+ export interface ScheduleDeliveryListResult {
70
+ deliveries: ScheduleDelivery[];
71
+ }
72
+
73
+ export class ScheduleService {
74
+ #adapter: FetchAdapter;
75
+ #baseUrl: string;
76
+
77
+ constructor(baseUrl: string, adapter: FetchAdapter) {
78
+ this.#adapter = adapter;
79
+ this.#baseUrl = baseUrl;
80
+ }
81
+
82
+ async create(params: CreateScheduleParams): Promise<ScheduleCreateResult> {
83
+ const url = buildUrl(this.#baseUrl, '/schedule/create/2026-02-24');
84
+ const signal = AbortSignal.timeout(30_000);
85
+ const res = await this.#adapter.invoke<ScheduleCreateResult>(url, {
86
+ method: 'POST',
87
+ signal,
88
+ body: JSON.stringify(params),
89
+ contentType: 'application/json',
90
+ telemetry: {
91
+ name: 'agentuity.schedule.create',
92
+ attributes: {
93
+ destinationCount: String(params.destinations?.length ?? 0),
94
+ name: params.name,
95
+ },
96
+ },
97
+ });
98
+
99
+ if (res.ok) {
100
+ return res.data;
101
+ }
102
+
103
+ throw await toServiceException('POST', url, res.response);
104
+ }
105
+
106
+ async list(params?: { limit?: number; offset?: number }): Promise<ScheduleListResult> {
107
+ const qs = new URLSearchParams();
108
+ if (params?.limit !== undefined) {
109
+ qs.set('limit', String(params.limit));
110
+ }
111
+ if (params?.offset !== undefined) {
112
+ qs.set('offset', String(params.offset));
113
+ }
114
+
115
+ const path = qs.toString()
116
+ ? `/schedule/list/2026-02-24?${qs.toString()}`
117
+ : '/schedule/list/2026-02-24';
118
+ const url = buildUrl(this.#baseUrl, path);
119
+ const signal = AbortSignal.timeout(30_000);
120
+ const res = await this.#adapter.invoke<ScheduleListResult>(url, {
121
+ method: 'GET',
122
+ signal,
123
+ telemetry: {
124
+ name: 'agentuity.schedule.list',
125
+ attributes: {
126
+ limit: String(params?.limit ?? ''),
127
+ offset: String(params?.offset ?? ''),
128
+ },
129
+ },
130
+ });
131
+
132
+ if (res.ok) {
133
+ return res.data;
134
+ }
135
+
136
+ throw await toServiceException('GET', url, res.response);
137
+ }
138
+
139
+ async get(scheduleId: string): Promise<ScheduleGetResult> {
140
+ const url = buildUrl(
141
+ this.#baseUrl,
142
+ `/schedule/get/2026-02-24/${encodeURIComponent(scheduleId)}`
143
+ );
144
+ const signal = AbortSignal.timeout(30_000);
145
+ const res = await this.#adapter.invoke<ScheduleGetResult>(url, {
146
+ method: 'GET',
147
+ signal,
148
+ telemetry: {
149
+ name: 'agentuity.schedule.get',
150
+ attributes: {
151
+ scheduleId,
152
+ },
153
+ },
154
+ });
155
+
156
+ if (res.ok) {
157
+ return res.data;
158
+ }
159
+
160
+ if (res.response.status === 404) {
161
+ throw await toServiceException('GET', url, res.response);
162
+ }
163
+
164
+ throw await toServiceException('GET', url, res.response);
165
+ }
166
+
167
+ async update(scheduleId: string, params: UpdateScheduleParams): Promise<{ schedule: Schedule }> {
168
+ const url = buildUrl(
169
+ this.#baseUrl,
170
+ `/schedule/update/2026-02-24/${encodeURIComponent(scheduleId)}`
171
+ );
172
+ const signal = AbortSignal.timeout(30_000);
173
+ const res = await this.#adapter.invoke<{ schedule: Schedule }>(url, {
174
+ method: 'PUT',
175
+ signal,
176
+ body: JSON.stringify(params),
177
+ contentType: 'application/json',
178
+ telemetry: {
179
+ name: 'agentuity.schedule.update',
180
+ attributes: {
181
+ scheduleId,
182
+ },
183
+ },
184
+ });
185
+
186
+ if (res.ok) {
187
+ return res.data;
188
+ }
189
+
190
+ throw await toServiceException('PUT', url, res.response);
191
+ }
192
+
193
+ async delete(scheduleId: string): Promise<void> {
194
+ const url = buildUrl(
195
+ this.#baseUrl,
196
+ `/schedule/delete/2026-02-24/${encodeURIComponent(scheduleId)}`
197
+ );
198
+ const signal = AbortSignal.timeout(30_000);
199
+ const res = await this.#adapter.invoke<void>(url, {
200
+ method: 'DELETE',
201
+ signal,
202
+ telemetry: {
203
+ name: 'agentuity.schedule.delete',
204
+ attributes: {
205
+ scheduleId,
206
+ },
207
+ },
208
+ });
209
+
210
+ if (res.ok) {
211
+ return;
212
+ }
213
+
214
+ throw await toServiceException('DELETE', url, res.response);
215
+ }
216
+
217
+ async createDestination(
218
+ scheduleId: string,
219
+ params: CreateScheduleDestinationParams
220
+ ): Promise<{ destination: ScheduleDestination }> {
221
+ const url = buildUrl(
222
+ this.#baseUrl,
223
+ `/schedule/destinations/create/2026-02-24/${encodeURIComponent(scheduleId)}`
224
+ );
225
+ const signal = AbortSignal.timeout(30_000);
226
+ const res = await this.#adapter.invoke<{ destination: ScheduleDestination }>(url, {
227
+ method: 'POST',
228
+ signal,
229
+ body: JSON.stringify(params),
230
+ contentType: 'application/json',
231
+ telemetry: {
232
+ name: 'agentuity.schedule.createDestination',
233
+ attributes: {
234
+ scheduleId,
235
+ type: params.type,
236
+ },
237
+ },
238
+ });
239
+
240
+ if (res.ok) {
241
+ return res.data;
242
+ }
243
+
244
+ throw await toServiceException('POST', url, res.response);
245
+ }
246
+
247
+ async deleteDestination(destinationId: string): Promise<void> {
248
+ const url = buildUrl(
249
+ this.#baseUrl,
250
+ `/schedule/destinations/delete/2026-02-24/${encodeURIComponent(destinationId)}`
251
+ );
252
+ const signal = AbortSignal.timeout(30_000);
253
+ const res = await this.#adapter.invoke<void>(url, {
254
+ method: 'DELETE',
255
+ signal,
256
+ telemetry: {
257
+ name: 'agentuity.schedule.deleteDestination',
258
+ attributes: {
259
+ destinationId,
260
+ },
261
+ },
262
+ });
263
+
264
+ if (res.ok) {
265
+ return;
266
+ }
267
+
268
+ throw await toServiceException('DELETE', url, res.response);
269
+ }
270
+
271
+ async listDeliveries(
272
+ scheduleId: string,
273
+ params?: { limit?: number; offset?: number }
274
+ ): Promise<ScheduleDeliveryListResult> {
275
+ const qs = new URLSearchParams();
276
+ if (params?.limit !== undefined) {
277
+ qs.set('limit', String(params.limit));
278
+ }
279
+ if (params?.offset !== undefined) {
280
+ qs.set('offset', String(params.offset));
281
+ }
282
+
283
+ const basePath = `/schedule/deliveries/2026-02-24/${encodeURIComponent(scheduleId)}`;
284
+ const path = qs.toString() ? `${basePath}?${qs.toString()}` : basePath;
285
+ const url = buildUrl(this.#baseUrl, path);
286
+ const signal = AbortSignal.timeout(30_000);
287
+ const res = await this.#adapter.invoke<ScheduleDeliveryListResult>(url, {
288
+ method: 'GET',
289
+ signal,
290
+ telemetry: {
291
+ name: 'agentuity.schedule.listDeliveries',
292
+ attributes: {
293
+ scheduleId,
294
+ limit: String(params?.limit ?? ''),
295
+ offset: String(params?.offset ?? ''),
296
+ },
297
+ },
298
+ });
299
+
300
+ if (res.ok) {
301
+ return res.data;
302
+ }
303
+
304
+ if (res.response.status === 404) {
305
+ throw await toServiceException('GET', url, res.response);
306
+ }
307
+
308
+ throw await toServiceException('GET', url, res.response);
309
+ }
310
+
311
+ async getDestination(scheduleId: string, destinationId: string): Promise<ScheduleDestination> {
312
+ const result = await this.get(scheduleId);
313
+ const destination = result.destinations.find((d) => d.id === destinationId);
314
+ if (!destination) {
315
+ throw new Error(`Destination not found: ${destinationId}`);
316
+ }
317
+ return destination;
318
+ }
319
+
320
+ async getDelivery(
321
+ scheduleId: string,
322
+ deliveryId: string,
323
+ params?: { limit?: number; offset?: number }
324
+ ): Promise<ScheduleDelivery> {
325
+ const result = await this.listDeliveries(scheduleId, params);
326
+ const delivery = result.deliveries.find((d) => d.id === deliveryId);
327
+ if (!delivery) {
328
+ throw new Error(`Delivery not found: ${deliveryId}`);
329
+ }
330
+ return delivery;
331
+ }
332
+ }
@@ -0,0 +1,366 @@
1
+ import { FetchAdapter } from './adapter.ts';
2
+ import { buildUrl, toServiceException } from './_util.ts';
3
+ import { StructuredError } from '../error.ts';
4
+ import { safeStringify } from '../json.ts';
5
+
6
+ // Task type enums
7
+ export type TaskPriority = 'high' | 'medium' | 'low' | 'none';
8
+ export type TaskType = 'epic' | 'feature' | 'enhancement' | 'bug' | 'task';
9
+ export type TaskStatus = 'open' | 'in_progress' | 'closed';
10
+
11
+ // Task object (returned from API)
12
+ export interface Task {
13
+ id: string;
14
+ created_at: string;
15
+ updated_at: string;
16
+ title: string;
17
+ description?: string;
18
+ metadata?: Record<string, unknown>;
19
+ priority: TaskPriority;
20
+ parent_id?: string;
21
+ type: TaskType;
22
+ status: TaskStatus;
23
+ open_date?: string;
24
+ in_progress_date?: string;
25
+ closed_date?: string;
26
+ created_id: string;
27
+ assigned_id?: string;
28
+ closed_id?: string;
29
+ }
30
+
31
+ // Changelog entry
32
+ export interface TaskChangelogEntry {
33
+ id: string;
34
+ created_at: string;
35
+ task_id: string;
36
+ field: string;
37
+ old_value?: string;
38
+ new_value?: string;
39
+ }
40
+
41
+ // Request params
42
+ export interface CreateTaskParams {
43
+ title: string;
44
+ description?: string;
45
+ metadata?: Record<string, unknown>;
46
+ priority?: TaskPriority;
47
+ parent_id?: string;
48
+ type: TaskType;
49
+ status?: TaskStatus;
50
+ created_id: string;
51
+ assigned_id?: string;
52
+ }
53
+
54
+ export interface UpdateTaskParams {
55
+ title?: string;
56
+ description?: string;
57
+ metadata?: Record<string, unknown>;
58
+ priority?: TaskPriority;
59
+ parent_id?: string;
60
+ type?: TaskType;
61
+ status?: TaskStatus;
62
+ assigned_id?: string;
63
+ closed_id?: string;
64
+ }
65
+
66
+ export interface ListTasksParams {
67
+ status?: TaskStatus;
68
+ type?: TaskType;
69
+ priority?: TaskPriority;
70
+ assigned_id?: string;
71
+ parent_id?: string;
72
+ sort?: string;
73
+ order?: 'asc' | 'desc';
74
+ limit?: number;
75
+ offset?: number;
76
+ }
77
+
78
+ export interface ListTasksResult {
79
+ tasks: Task[];
80
+ total: number;
81
+ limit: number;
82
+ offset: number;
83
+ }
84
+
85
+ export interface TaskChangelogResult {
86
+ changelog: TaskChangelogEntry[];
87
+ total: number;
88
+ limit: number;
89
+ offset: number;
90
+ }
91
+
92
+ export interface TaskStorage {
93
+ create(params: CreateTaskParams): Promise<Task>;
94
+ get(id: string): Promise<Task | null>;
95
+ list(params?: ListTasksParams): Promise<ListTasksResult>;
96
+ update(id: string, params: UpdateTaskParams): Promise<Task>;
97
+ close(id: string): Promise<Task>;
98
+ changelog(
99
+ id: string,
100
+ params?: { limit?: number; offset?: number }
101
+ ): Promise<TaskChangelogResult>;
102
+ }
103
+
104
+ const TASK_API_VERSION = '2026-02-24';
105
+
106
+ const TaskIdRequiredError = StructuredError(
107
+ 'TaskIdRequiredError',
108
+ 'Task ID is required and must be a non-empty string'
109
+ );
110
+
111
+ const TaskTitleRequiredError = StructuredError(
112
+ 'TaskTitleRequiredError',
113
+ 'Task title is required and must be a non-empty string'
114
+ );
115
+
116
+ const TaskStorageResponseError = StructuredError('TaskStorageResponseError')<{
117
+ status: number;
118
+ }>();
119
+
120
+ interface TaskSuccessResponse<T> {
121
+ success: true;
122
+ data: T;
123
+ }
124
+
125
+ interface TaskErrorResponse {
126
+ success: false;
127
+ message: string;
128
+ }
129
+
130
+ type TaskResponse<T> = TaskSuccessResponse<T> | TaskErrorResponse;
131
+
132
+ export class TaskStorageService implements TaskStorage {
133
+ #adapter: FetchAdapter;
134
+ #baseUrl: string;
135
+
136
+ constructor(baseUrl: string, adapter: FetchAdapter) {
137
+ this.#adapter = adapter;
138
+ this.#baseUrl = baseUrl;
139
+ }
140
+
141
+ async create(params: CreateTaskParams): Promise<Task> {
142
+ if (!params?.title || typeof params.title !== 'string' || params.title.trim().length === 0) {
143
+ throw new TaskTitleRequiredError();
144
+ }
145
+
146
+ const url = buildUrl(this.#baseUrl, `/task/${TASK_API_VERSION}`);
147
+ const signal = AbortSignal.timeout(30_000);
148
+
149
+ const res = await this.#adapter.invoke<TaskResponse<Task>>(url, {
150
+ method: 'POST',
151
+ body: safeStringify(params),
152
+ contentType: 'application/json',
153
+ signal,
154
+ telemetry: {
155
+ name: 'agentuity.task.create',
156
+ attributes: {
157
+ type: params.type,
158
+ priority: params.priority ?? 'none',
159
+ status: params.status ?? 'open',
160
+ },
161
+ },
162
+ });
163
+
164
+ if (res.ok) {
165
+ if (res.data.success) {
166
+ return res.data.data;
167
+ }
168
+ throw new TaskStorageResponseError({
169
+ status: res.response.status,
170
+ message: res.data.message,
171
+ });
172
+ }
173
+
174
+ throw await toServiceException('POST', url, res.response);
175
+ }
176
+
177
+ async get(id: string): Promise<Task | null> {
178
+ if (!id || typeof id !== 'string' || id.trim().length === 0) {
179
+ throw new TaskIdRequiredError();
180
+ }
181
+
182
+ const url = buildUrl(this.#baseUrl, `/task/${TASK_API_VERSION}/${encodeURIComponent(id)}`);
183
+ const signal = AbortSignal.timeout(30_000);
184
+
185
+ const res = await this.#adapter.invoke<TaskResponse<Task>>(url, {
186
+ method: 'GET',
187
+ signal,
188
+ telemetry: {
189
+ name: 'agentuity.task.get',
190
+ attributes: { id },
191
+ },
192
+ });
193
+
194
+ if (res.response.status === 404) {
195
+ return null;
196
+ }
197
+
198
+ if (res.ok) {
199
+ if (res.data.success) {
200
+ return res.data.data;
201
+ }
202
+ throw new TaskStorageResponseError({
203
+ status: res.response.status,
204
+ message: res.data.message,
205
+ });
206
+ }
207
+
208
+ throw await toServiceException('GET', url, res.response);
209
+ }
210
+
211
+ async list(params?: ListTasksParams): Promise<ListTasksResult> {
212
+ const queryParams = new URLSearchParams();
213
+ if (params?.status) queryParams.set('status', params.status);
214
+ if (params?.type) queryParams.set('type', params.type);
215
+ if (params?.priority) queryParams.set('priority', params.priority);
216
+ if (params?.assigned_id) queryParams.set('assigned_id', params.assigned_id);
217
+ if (params?.parent_id) queryParams.set('parent_id', params.parent_id);
218
+ if (params?.sort) queryParams.set('sort', params.sort);
219
+ if (params?.order) queryParams.set('order', params.order);
220
+ if (params?.limit !== undefined) queryParams.set('limit', String(params.limit));
221
+ if (params?.offset !== undefined) queryParams.set('offset', String(params.offset));
222
+
223
+ const queryString = queryParams.toString();
224
+ const url = buildUrl(
225
+ this.#baseUrl,
226
+ `/task/${TASK_API_VERSION}${queryString ? `?${queryString}` : ''}`
227
+ );
228
+ const signal = AbortSignal.timeout(30_000);
229
+
230
+ const res = await this.#adapter.invoke<TaskResponse<ListTasksResult>>(url, {
231
+ method: 'GET',
232
+ signal,
233
+ telemetry: {
234
+ name: 'agentuity.task.list',
235
+ attributes: {
236
+ ...(params?.status ? { status: params.status } : {}),
237
+ ...(params?.type ? { type: params.type } : {}),
238
+ ...(params?.priority ? { priority: params.priority } : {}),
239
+ },
240
+ },
241
+ });
242
+
243
+ if (res.ok) {
244
+ if (res.data.success) {
245
+ return res.data.data;
246
+ }
247
+ throw new TaskStorageResponseError({
248
+ status: res.response.status,
249
+ message: res.data.message,
250
+ });
251
+ }
252
+
253
+ throw await toServiceException('GET', url, res.response);
254
+ }
255
+
256
+ async update(id: string, params: UpdateTaskParams): Promise<Task> {
257
+ if (!id || typeof id !== 'string' || id.trim().length === 0) {
258
+ throw new TaskIdRequiredError();
259
+ }
260
+ if (
261
+ params.title !== undefined &&
262
+ (typeof params.title !== 'string' || params.title.trim().length === 0)
263
+ ) {
264
+ throw new TaskTitleRequiredError();
265
+ }
266
+
267
+ const url = buildUrl(this.#baseUrl, `/task/${TASK_API_VERSION}/${encodeURIComponent(id)}`);
268
+ const signal = AbortSignal.timeout(30_000);
269
+
270
+ const res = await this.#adapter.invoke<TaskResponse<Task>>(url, {
271
+ method: 'PATCH',
272
+ body: safeStringify(params),
273
+ contentType: 'application/json',
274
+ signal,
275
+ telemetry: {
276
+ name: 'agentuity.task.update',
277
+ attributes: { id },
278
+ },
279
+ });
280
+
281
+ if (res.ok) {
282
+ if (res.data.success) {
283
+ return res.data.data;
284
+ }
285
+ throw new TaskStorageResponseError({
286
+ status: res.response.status,
287
+ message: res.data.message,
288
+ });
289
+ }
290
+
291
+ throw await toServiceException('PATCH', url, res.response);
292
+ }
293
+
294
+ async close(id: string): Promise<Task> {
295
+ if (!id || typeof id !== 'string' || id.trim().length === 0) {
296
+ throw new TaskIdRequiredError();
297
+ }
298
+
299
+ const url = buildUrl(this.#baseUrl, `/task/${TASK_API_VERSION}/${encodeURIComponent(id)}`);
300
+ const signal = AbortSignal.timeout(30_000);
301
+
302
+ const res = await this.#adapter.invoke<TaskResponse<Task>>(url, {
303
+ method: 'DELETE',
304
+ signal,
305
+ telemetry: {
306
+ name: 'agentuity.task.close',
307
+ attributes: { id },
308
+ },
309
+ });
310
+
311
+ if (res.ok) {
312
+ if (res.data.success) {
313
+ return res.data.data;
314
+ }
315
+ throw new TaskStorageResponseError({
316
+ status: res.response.status,
317
+ message: res.data.message,
318
+ });
319
+ }
320
+
321
+ throw await toServiceException('DELETE', url, res.response);
322
+ }
323
+
324
+ async changelog(
325
+ id: string,
326
+ params?: { limit?: number; offset?: number }
327
+ ): Promise<TaskChangelogResult> {
328
+ if (!id || typeof id !== 'string' || id.trim().length === 0) {
329
+ throw new TaskIdRequiredError();
330
+ }
331
+
332
+ const queryParams = new URLSearchParams();
333
+ if (params?.limit !== undefined) queryParams.set('limit', String(params.limit));
334
+ if (params?.offset !== undefined) queryParams.set('offset', String(params.offset));
335
+ const queryString = queryParams.toString();
336
+
337
+ const url = buildUrl(
338
+ this.#baseUrl,
339
+ `/task-changelog/${TASK_API_VERSION}/${encodeURIComponent(id)}${
340
+ queryString ? `?${queryString}` : ''
341
+ }`
342
+ );
343
+ const signal = AbortSignal.timeout(30_000);
344
+
345
+ const res = await this.#adapter.invoke<TaskResponse<TaskChangelogResult>>(url, {
346
+ method: 'GET',
347
+ signal,
348
+ telemetry: {
349
+ name: 'agentuity.task.changelog',
350
+ attributes: { id },
351
+ },
352
+ });
353
+
354
+ if (res.ok) {
355
+ if (res.data.success) {
356
+ return res.data.data;
357
+ }
358
+ throw new TaskStorageResponseError({
359
+ status: res.response.status,
360
+ message: res.data.message,
361
+ });
362
+ }
363
+
364
+ throw await toServiceException('GET', url, res.response);
365
+ }
366
+ }