@agent-native/core 0.157.7 → 0.157.8

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.
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
- ok?: undefined;
46
44
  summary: import("./types.js").TraceSummary;
47
45
  spans: import("./types.js").TraceSpan[];
48
46
  id?: undefined;
49
- } | {
50
47
  error?: undefined;
51
48
  ok?: undefined;
49
+ } | {
52
50
  summary?: undefined;
53
51
  spans?: undefined;
54
52
  id: string;
55
- } | {
53
+ error?: undefined;
56
54
  ok?: undefined;
55
+ } | {
57
56
  summary?: undefined;
58
57
  spans?: undefined;
59
58
  id?: undefined;
60
59
  error: any;
60
+ ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
65
+ error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- error: string;
52
51
  ok?: undefined;
52
+ error: string;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- ok?: undefined;
38
37
  status?: undefined;
38
+ ok?: undefined;
39
39
  } | {
40
- error?: undefined;
41
40
  ok: boolean;
42
41
  status: string;
42
+ error?: undefined;
43
43
  } | {
44
- ok?: undefined;
45
44
  error: string;
46
45
  removed?: undefined;
46
+ ok?: undefined;
47
47
  } | {
48
- error?: undefined;
49
48
  ok: boolean;
50
49
  removed: boolean;
50
+ error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
54
54
  * or the current stored value without changing anything.
55
55
  */
56
56
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
- ok?: undefined;
58
57
  error: string;
59
58
  note?: undefined;
59
+ ok?: undefined;
60
60
  } | {
61
- error?: undefined;
62
61
  ok: boolean;
63
62
  note?: undefined;
64
- } | {
65
63
  error?: undefined;
64
+ } | {
66
65
  ok: boolean;
67
66
  note: string;
67
+ error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
- error?: undefined;
99
98
  ok: boolean;
100
99
  key: string;
101
- } | {
102
100
  error?: undefined;
101
+ } | {
103
102
  ok: boolean;
104
103
  removed: boolean;
104
+ error?: undefined;
105
105
  }>>;
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
27
27
  export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
28
28
  error: any;
29
29
  } | {
30
- error?: undefined;
31
30
  ok: boolean;
32
31
  key: string;
33
32
  baseUrlKey?: string;
34
33
  scope: AgentEngineApiKeyScope;
34
+ error?: undefined;
35
35
  }>>;
36
36
  export {};
@@ -233,10 +233,10 @@ export declare class AppSyncState {
233
233
  * bounded index range scan.
234
234
  *
235
235
  * The `lastDurablePrune` throttle below is per-process, so every serverless
236
- * worker prunes on its first poll. That was survivable only once the work
237
- * itself became bounded and indexed; if concurrency is still a problem,
238
- * the next step is a `pg_try_advisory_xact_lock` so one worker prunes at a
239
- * time. Deliberately not done yet — measure before adding a lock.
236
+ * worker prunes on its first poll. Postgres workers also take a
237
+ * transaction-scoped advisory lease for each batch: only one worker deletes
238
+ * at a time, but each batch still commits independently so a killed worker
239
+ * cannot hold a long transaction open.
240
240
  */
241
241
  private pruneDurableEvents;
242
242
  persistSyncEvent(event: ChangeEvent, dedupeKey?: string, presetId?: string): Promise<void>;
@@ -42,6 +42,12 @@ const DURABLE_RETENTION_MS = 24 * 60 * 60 * 1000;
42
42
  */
43
43
  const DURABLE_PRUNE_BATCH = 10_000;
44
44
  const DURABLE_PRUNE_MAX_BATCHES = 40;
45
+ /**
46
+ * Postgres advisory-lock key for the per-database retention worker. The lock
47
+ * is transaction-scoped so a serverless worker that is killed cannot leave a
48
+ * lease behind for another worker to clear.
49
+ */
50
+ const DURABLE_PRUNE_LOCK_KEY = "agent-native:sync-events-prune";
45
51
  /**
46
52
  * How far back a cold-started process replays durable action markers.
47
53
  * Covers the gap between a separate action process writing its marker and this
@@ -455,10 +461,10 @@ export class AppSyncState {
455
461
  * bounded index range scan.
456
462
  *
457
463
  * The `lastDurablePrune` throttle below is per-process, so every serverless
458
- * worker prunes on its first poll. That was survivable only once the work
459
- * itself became bounded and indexed; if concurrency is still a problem,
460
- * the next step is a `pg_try_advisory_xact_lock` so one worker prunes at a
461
- * time. Deliberately not done yet — measure before adding a lock.
464
+ * worker prunes on its first poll. Postgres workers also take a
465
+ * transaction-scoped advisory lease for each batch: only one worker deletes
466
+ * at a time, but each batch still commits independently so a killed worker
467
+ * cannot hold a long transaction open.
462
468
  */
463
469
  async pruneDurableEvents(client) {
464
470
  const now = Date.now();
@@ -469,17 +475,43 @@ export class AppSyncState {
469
475
  let deleted = 0;
470
476
  try {
471
477
  for (let batch = 0; batch < DURABLE_PRUNE_MAX_BATCHES; batch++) {
472
- const result = await client.execute({
478
+ const runBatch = async (tx) => {
479
+ if (this.isPg()) {
480
+ const lockResult = await tx.execute({
481
+ sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
482
+ args: [DURABLE_PRUNE_LOCK_KEY],
483
+ });
484
+ const acquired = lockResult.rows[0]?.acquired;
485
+ if (acquired !== true && acquired !== "t")
486
+ return null;
487
+ }
473
488
  // `id IN (...)` rather than ctid/rowid: both are dialect-specific,
474
489
  // and the primary key is indexed on every dialect we ship.
475
- sql: `DELETE FROM sync_events WHERE id IN (
476
- SELECT id FROM sync_events WHERE created_at < ?
477
- ORDER BY created_at, id LIMIT ?
478
- )`,
479
- args: [cutoff, DURABLE_PRUNE_BATCH],
480
- });
481
- deleted += result.rowsAffected;
482
- if (result.rowsAffected < DURABLE_PRUNE_BATCH)
490
+ const result = await tx.execute({
491
+ sql: `DELETE FROM sync_events WHERE id IN (
492
+ SELECT id FROM sync_events WHERE created_at < ?
493
+ ORDER BY created_at, id LIMIT ?
494
+ )`,
495
+ args: [cutoff, DURABLE_PRUNE_BATCH],
496
+ });
497
+ return result.rowsAffected;
498
+ };
499
+ // A Postgres advisory transaction lock is only useful when the lock
500
+ // and delete share a connection and transaction. Never fall back to
501
+ // an uncoordinated delete on a transactionless client.
502
+ const rowsAffected = this.isPg()
503
+ ? client.transaction
504
+ ? await client.transaction(runBatch)
505
+ : null
506
+ : await runBatch(client);
507
+ if (rowsAffected == null) {
508
+ if (this.isPg() && !client.transaction) {
509
+ throw new Error("Postgres prune requires a database transaction");
510
+ }
511
+ break;
512
+ }
513
+ deleted += rowsAffected;
514
+ if (rowsAffected < DURABLE_PRUNE_BATCH)
483
515
  break;
484
516
  }
485
517
  this.durablePruneFailures = 0;
@@ -20,6 +20,6 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- text: string;
24
23
  error?: undefined;
24
+ text: string;
25
25
  }>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.157.7",
3
+ "version": "0.157.8",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {