@agentuity/runtime 1.0.30 → 1.0.32

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 (44) hide show
  1. package/dist/_metadata.js +9 -1
  2. package/dist/_metadata.js.map +1 -1
  3. package/dist/_process-protection.js +1 -1
  4. package/dist/_process-protection.js.map +1 -1
  5. package/dist/_tokens.js +1 -1
  6. package/dist/_tokens.js.map +1 -1
  7. package/dist/dev-patches/aisdk.js +9 -1
  8. package/dist/dev-patches/aisdk.js.map +1 -1
  9. package/dist/dev-patches/otel-llm.js +9 -1
  10. package/dist/dev-patches/otel-llm.js.map +1 -1
  11. package/dist/eval.d.ts +2 -2
  12. package/dist/eval.d.ts.map +1 -1
  13. package/dist/eval.js.map +1 -1
  14. package/dist/handlers/cron.js +1 -1
  15. package/dist/handlers/cron.js.map +1 -1
  16. package/dist/middleware.js +1 -1
  17. package/dist/middleware.js.map +1 -1
  18. package/dist/otel/logger.js +8 -8
  19. package/dist/otel/logger.js.map +1 -1
  20. package/dist/router.d.ts +1 -1
  21. package/dist/router.d.ts.map +1 -1
  22. package/dist/router.js +32 -33
  23. package/dist/router.js.map +1 -1
  24. package/dist/services/local/queue.d.ts +3 -1
  25. package/dist/services/local/queue.d.ts.map +1 -1
  26. package/dist/services/local/queue.js +10 -0
  27. package/dist/services/local/queue.js.map +1 -1
  28. package/dist/services/local/task.d.ts +2 -1
  29. package/dist/services/local/task.d.ts.map +1 -1
  30. package/dist/services/local/task.js +91 -0
  31. package/dist/services/local/task.js.map +1 -1
  32. package/dist/signature.js +1 -1
  33. package/dist/signature.js.map +1 -1
  34. package/package.json +7 -7
  35. package/src/_process-protection.ts +1 -1
  36. package/src/_tokens.ts +1 -1
  37. package/src/eval.ts +2 -3
  38. package/src/handlers/cron.ts +1 -1
  39. package/src/middleware.ts +1 -1
  40. package/src/otel/logger.ts +8 -8
  41. package/src/router.ts +33 -34
  42. package/src/services/local/queue.ts +19 -1
  43. package/src/services/local/task.ts +123 -0
  44. package/src/signature.ts +1 -1
package/src/router.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { type Context, Hono, type Schema, type Env as HonoEnv } from 'hono';
2
+ import { type Context, Hono, type Env as HonoEnv, type Schema } from 'hono';
3
+ import { loadBuildMetadata } from './_metadata';
3
4
  import { returnResponse } from './_util';
4
5
  import type { Env } from './app';
5
- import { loadBuildMetadata } from './_metadata';
6
6
 
7
7
  // Re-export both Env types
8
8
  export type { Env };
@@ -134,7 +134,6 @@ declare module 'hono' {
134
134
  export const createRouter = <E extends Env = Env, S extends Schema = Schema>(): Hono<E, S> => {
135
135
  const router = new Hono<E, S>();
136
136
  // tslint:disable-next-line:no-any no-unused-variable
137
- // biome-ignore lint:no-any
138
137
  const _router = router as any;
139
138
 
140
139
  for (const method of ['get', 'put', 'post', 'delete', 'options', 'patch']) {
@@ -211,68 +210,68 @@ export const createRouter = <E extends Env = Env, S extends Schema = Schema>():
211
210
  // Deprecated stubs that throw errors with migration instructions
212
211
  _router.websocket = (path: string, ..._args: any[]) => {
213
212
  throw new Error(
214
- `router.websocket() is deprecated and has been removed.\n\n` +
215
- `Migration: Use the websocket middleware instead:\n\n` +
213
+ 'router.websocket() is deprecated and has been removed.\n\n' +
214
+ 'Migration: Use the websocket middleware instead:\n\n' +
216
215
  ` import { createRouter, websocket } from '@agentuity/runtime';\n\n` +
217
- ` const router = createRouter();\n\n` +
218
- ` // Before (deprecated):\n` +
216
+ ' const router = createRouter();\n\n' +
217
+ ' // Before (deprecated):\n' +
219
218
  ` // router.websocket('${path}', (c) => (ws) => { ... });\n\n` +
220
- ` // After:\n` +
219
+ ' // After:\n' +
221
220
  ` router.get('${path}', websocket((c, ws) => {\n` +
222
- ` ws.onMessage((event) => {\n` +
221
+ ' ws.onMessage((event) => {\n' +
223
222
  ` ws.send('Echo: ' + event.data);\n` +
224
- ` });\n` +
225
- ` }));`
223
+ ' });\n' +
224
+ ' }));'
226
225
  );
227
226
  };
228
227
 
229
228
  _router.sse = (path: string, ..._args: any[]) => {
230
229
  throw new Error(
231
- `router.sse() is deprecated and has been removed.\n\n` +
232
- `Migration: Use the sse middleware instead:\n\n` +
230
+ 'router.sse() is deprecated and has been removed.\n\n' +
231
+ 'Migration: Use the sse middleware instead:\n\n' +
233
232
  ` import { createRouter, sse } from '@agentuity/runtime';\n\n` +
234
- ` const router = createRouter();\n\n` +
235
- ` // Before (deprecated):\n` +
233
+ ' const router = createRouter();\n\n' +
234
+ ' // Before (deprecated):\n' +
236
235
  ` // router.sse('${path}', (c) => async (stream) => { ... });\n\n` +
237
- ` // After:\n` +
236
+ ' // After:\n' +
238
237
  ` router.get('${path}', sse((c, stream) => {\n` +
239
238
  ` stream.writeSSE({ data: 'Hello', event: 'message' });\n` +
240
- ` }));`
239
+ ' }));'
241
240
  );
242
241
  };
243
242
 
244
243
  _router.stream = (path: string, ..._args: any[]) => {
245
244
  throw new Error(
246
- `router.stream() is deprecated and has been removed.\n\n` +
247
- `Migration: Use the stream middleware instead:\n\n` +
245
+ 'router.stream() is deprecated and has been removed.\n\n' +
246
+ 'Migration: Use the stream middleware instead:\n\n' +
248
247
  ` import { createRouter, stream } from '@agentuity/runtime';\n\n` +
249
- ` const router = createRouter();\n\n` +
250
- ` // Before (deprecated):\n` +
248
+ ' const router = createRouter();\n\n' +
249
+ ' // Before (deprecated):\n' +
251
250
  ` // router.stream('${path}', (c) => new ReadableStream({ ... }));\n\n` +
252
- ` // After:\n` +
251
+ ' // After:\n' +
253
252
  ` router.post('${path}', stream((c) => {\n` +
254
- ` return new ReadableStream({\n` +
255
- ` start(controller) {\n` +
253
+ ' return new ReadableStream({\n' +
254
+ ' start(controller) {\n' +
256
255
  ` controller.enqueue('data\\n');\n` +
257
- ` controller.close();\n` +
258
- ` }\n` +
259
- ` });\n` +
260
- ` }));`
256
+ ' controller.close();\n' +
257
+ ' }\n' +
258
+ ' });\n' +
259
+ ' }));'
261
260
  );
262
261
  };
263
262
 
264
263
  _router.cron = (schedule: string, ..._args: any[]) => {
265
264
  throw new Error(
266
- `router.cron() is deprecated and has been removed.\n\n` +
267
- `Migration: Use the cron middleware instead:\n\n` +
265
+ 'router.cron() is deprecated and has been removed.\n\n' +
266
+ 'Migration: Use the cron middleware instead:\n\n' +
268
267
  ` import { createRouter, cron } from '@agentuity/runtime';\n\n` +
269
- ` const router = createRouter();\n\n` +
270
- ` // Before (deprecated):\n` +
268
+ ' const router = createRouter();\n\n' +
269
+ ' // Before (deprecated):\n' +
271
270
  ` // router.cron('${schedule}', (c) => { ... });\n\n` +
272
- ` // After:\n` +
271
+ ' // After:\n' +
273
272
  ` router.post('/your-cron-path', cron('${schedule}', (c) => {\n` +
274
273
  ` return { status: 'complete' };\n` +
275
- ` }));`
274
+ ' }));'
276
275
  );
277
276
  };
278
277
 
@@ -1,5 +1,11 @@
1
1
  import type { Database } from 'bun:sqlite';
2
- import type { QueueService, QueuePublishParams, QueuePublishResult } from '@agentuity/core';
2
+ import type {
3
+ QueueService,
4
+ QueuePublishParams,
5
+ QueuePublishResult,
6
+ QueueCreateParams,
7
+ QueueCreateResult,
8
+ } from '@agentuity/core';
3
9
 
4
10
  export class LocalQueueStorage implements QueueService {
5
11
  #db: Database;
@@ -124,4 +130,16 @@ export class LocalQueueStorage implements QueueService {
124
130
 
125
131
  return publishInTransaction.immediate();
126
132
  }
133
+
134
+ async createQueue(queueName: string, params?: QueueCreateParams): Promise<QueueCreateResult> {
135
+ console.debug(`[local] createQueue: ${queueName}`);
136
+ return {
137
+ name: queueName,
138
+ queueType: params?.queueType ?? 'worker',
139
+ };
140
+ }
141
+
142
+ async deleteQueue(_queueName: string): Promise<void> {
143
+ // No-op in local mode — queues don't need provisioning locally
144
+ }
127
145
  }
@@ -10,6 +10,8 @@ import type {
10
10
  UpdateTaskParams,
11
11
  ListTasksParams,
12
12
  ListTasksResult,
13
+ BatchDeleteTasksParams,
14
+ BatchDeleteTasksResult,
13
15
  TaskChangelogResult,
14
16
  Comment,
15
17
  Tag,
@@ -120,6 +122,32 @@ const SORT_FIELDS: Record<string, string> = {
120
122
  closed_date: 'closed_date',
121
123
  };
122
124
 
125
+ const DURATION_UNITS: Record<string, number> = {
126
+ m: 60 * 1000,
127
+ h: 60 * 60 * 1000,
128
+ d: 24 * 60 * 60 * 1000,
129
+ w: 7 * 24 * 60 * 60 * 1000,
130
+ };
131
+
132
+ const InvalidDurationError = StructuredError(
133
+ 'InvalidDurationError',
134
+ 'Invalid duration format: use a number followed by m (minutes), h (hours), d (days), or w (weeks)'
135
+ );
136
+
137
+ function parseDurationMs(duration: string): number {
138
+ const match = duration.match(/^(\d+)([mhdw])$/);
139
+ if (!match) {
140
+ throw new InvalidDurationError();
141
+ }
142
+ const value = parseInt(match[1]!, 10);
143
+ const unit = match[2]!;
144
+ const ms = DURATION_UNITS[unit];
145
+ if (!ms) {
146
+ throw new InvalidDurationError();
147
+ }
148
+ return value * ms;
149
+ }
150
+
123
151
  function generateTaskId(): string {
124
152
  return `task_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
125
153
  }
@@ -716,6 +744,101 @@ export class LocalTaskStorage implements TaskStorage {
716
744
  return updated!;
717
745
  }
718
746
 
747
+ async batchDelete(params: BatchDeleteTasksParams): Promise<BatchDeleteTasksResult> {
748
+ const conditions: string[] = ['project_path = ?', 'deleted = 0'];
749
+ const args: (string | number)[] = [this.#projectPath];
750
+
751
+ if (params.status) {
752
+ conditions.push('status = ?');
753
+ args.push(params.status);
754
+ }
755
+ if (params.type) {
756
+ conditions.push('type = ?');
757
+ args.push(params.type);
758
+ }
759
+ if (params.priority) {
760
+ conditions.push('priority = ?');
761
+ args.push(params.priority);
762
+ }
763
+ if (params.parent_id) {
764
+ conditions.push('parent_id = ?');
765
+ args.push(params.parent_id);
766
+ }
767
+ if (params.created_id) {
768
+ conditions.push('created_id = ?');
769
+ args.push(params.created_id);
770
+ }
771
+ if (params.older_than) {
772
+ const ms = parseDurationMs(params.older_than);
773
+ const cutoff = Date.now() - ms;
774
+ conditions.push('created_at < ?');
775
+ args.push(cutoff);
776
+ }
777
+
778
+ // Require at least one filter beyond project_path + deleted
779
+ if (conditions.length < 3) {
780
+ const BatchDeleteFilterRequiredError = StructuredError(
781
+ 'BatchDeleteFilterRequiredError',
782
+ 'At least one filter is required for batch delete'
783
+ );
784
+ throw new BatchDeleteFilterRequiredError();
785
+ }
786
+
787
+ if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
788
+ const InvalidBatchDeleteLimitError = StructuredError(
789
+ 'InvalidBatchDeleteLimitError',
790
+ 'Batch delete limit must be a positive integer'
791
+ );
792
+ throw new InvalidBatchDeleteLimitError();
793
+ }
794
+ const limit = Math.min(params.limit ?? 50, 200);
795
+
796
+ const whereClause = conditions.join(' AND ');
797
+ const selectQuery = `SELECT id, title FROM task_storage WHERE ${whereClause} ORDER BY created_at ASC LIMIT ?`;
798
+ const selectStmt = this.#db.prepare(selectQuery);
799
+ const rows = selectStmt.all(...args, limit) as Array<{ id: string; title: string }>;
800
+
801
+ if (rows.length === 0) {
802
+ return { deleted: [], count: 0 };
803
+ }
804
+
805
+ const timestamp = now();
806
+ const ids = rows.map((r) => r.id);
807
+ const placeholders = ids.map(() => '?').join(', ');
808
+
809
+ const txn = this.#db.transaction(() => {
810
+ const updateStmt = this.#db.prepare(`
811
+ UPDATE task_storage
812
+ SET status = 'closed', deleted = 1, closed_date = COALESCE(closed_date, ?), updated_at = ?
813
+ WHERE project_path = ? AND id IN (${placeholders})
814
+ `);
815
+ updateStmt.run(new Date(timestamp).toISOString(), timestamp, this.#projectPath, ...ids);
816
+
817
+ const changelogStmt = this.#db.prepare(`
818
+ INSERT INTO task_changelog_storage (
819
+ project_path, id, task_id, field, old_value, new_value, created_at
820
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
821
+ `);
822
+ for (const row of rows) {
823
+ changelogStmt.run(
824
+ this.#projectPath,
825
+ generateChangelogId(),
826
+ row.id,
827
+ 'deleted',
828
+ 'false',
829
+ 'true',
830
+ timestamp
831
+ );
832
+ }
833
+ });
834
+ txn();
835
+
836
+ return {
837
+ deleted: rows.map((r) => ({ id: r.id, title: r.title })),
838
+ count: rows.length,
839
+ };
840
+ }
841
+
719
842
  async createComment(taskId: string, body: string, userId: string): Promise<Comment> {
720
843
  const trimmedBody = body?.trim();
721
844
  if (!trimmedBody) {
package/src/signature.ts CHANGED
@@ -48,7 +48,7 @@ export async function verifySignature(
48
48
  // Verify timestamp is within acceptable range (prevent replay attacks)
49
49
  const ts = parseInt(timestamp, 10);
50
50
  const now = Math.floor(Date.now() / 1000);
51
- if (isNaN(ts) || Math.abs(now - ts) > MAX_SIGNATURE_AGE_SECONDS) {
51
+ if (Number.isNaN(ts) || Math.abs(now - ts) > MAX_SIGNATURE_AGE_SECONDS) {
52
52
  return false;
53
53
  }
54
54