@agent-native/core 0.153.6 → 0.153.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.
@@ -681,13 +681,26 @@ export declare function listRunsForThread(threadId: string, options?: {
681
681
  * Best-effort on parse: malformed ledger rows are skipped rather than thrown.
682
682
  */
683
683
  export declare function getCurrentTurnEventsForThread(threadId: string, knownTurnId?: string): Promise<AgentChatEvent[]>;
684
+ export interface StaleRunReapResult {
685
+ /** Rows this pass reaped. */
686
+ reaped: number;
687
+ /**
688
+ * Rows whose reap threw. Separate from `reaped` because a pass where every
689
+ * row failed must not read as "nothing was stale" — those were the same
690
+ * number before, and the caller's own `null`-vs-`0` distinction is worthless
691
+ * while the per-row failures underneath it are coerced away.
692
+ */
693
+ failed: number;
694
+ /** More stale rows remain than the batch cap; the next tick continues. */
695
+ truncated: boolean;
696
+ }
684
697
  /**
685
698
  * Expire any "running" rows whose heartbeat is stale — producer died.
686
699
  * Safe to call at server startup on multi-isolate deployments: only rows
687
700
  * without a fresh heartbeat get reaped, so runs owned by OTHER live
688
701
  * isolates (which keep heartbeating) are left alone.
689
702
  */
690
- export declare function reapAllStaleRuns(): Promise<number>;
703
+ export declare function reapAllStaleRuns(): Promise<StaleRunReapResult>;
691
704
  /**
692
705
  * Read the daily terminal-outcome counters rolled up from pruned runs. Pair
693
706
  * with live `agent_runs` rows for a complete picture — see
@@ -2253,6 +2253,18 @@ export async function getCurrentTurnEventsForThread(threadId, knownTurnId) {
2253
2253
  }
2254
2254
  return events;
2255
2255
  }
2256
+ /**
2257
+ * Rows one pass will reap. Each costs ~5-10 serial round trips (reconcile,
2258
+ * transactional reap plus recovery, terminal-event append), and this sweep now
2259
+ * runs ahead of `processRecurringJobs` on the shared durable tick — so an
2260
+ * unbounded first pass against a backlog spends the platform wall before jobs
2261
+ * get any. Production carried 1,216 stale rows while nothing periodic reaped
2262
+ * them, so the first tick after that fix lands on exactly such a backlog.
2263
+ * A truncated pass is drained by the next tick (60s durable, 20s in-process),
2264
+ * never dropped — `truncated` is what stops a partial sweep being reported as
2265
+ * a clean one.
2266
+ */
2267
+ const REAP_ALL_STALE_BATCH_LIMIT = 200;
2256
2268
  /**
2257
2269
  * Expire any "running" rows whose heartbeat is stale — producer died.
2258
2270
  * Safe to call at server startup on multi-isolate deployments: only rows
@@ -2260,9 +2272,14 @@ export async function getCurrentTurnEventsForThread(threadId, knownTurnId) {
2260
2272
  * isolates (which keep heartbeating) are left alone.
2261
2273
  */
2262
2274
  export async function reapAllStaleRuns() {
2275
+ const nothingStale = {
2276
+ reaped: 0,
2277
+ failed: 0,
2278
+ truncated: false,
2279
+ };
2263
2280
  await ensureRunTables();
2264
2281
  if (!(await hasRunningRuns()))
2265
- return 0;
2282
+ return nothingStale;
2266
2283
  const client = getDbExec();
2267
2284
  const now = Date.now();
2268
2285
  // Background-dispatched runs use the wider window; everything else 15s. The
@@ -2274,13 +2291,23 @@ export async function reapAllStaleRuns() {
2274
2291
  // server startup across possibly-multiple isolates, so a sibling isolate's
2275
2292
  // still-alive, in-flight run must not be reaped just because THIS isolate
2276
2293
  // just booted and has no heartbeat history for it.
2277
- const stale = await client.execute({
2294
+ // One row over the cap is how a truncated pass is detected without a second
2295
+ // COUNT query against the same predicate.
2296
+ const scanned = await client.execute({
2278
2297
  sql: `SELECT id FROM agent_runs
2279
2298
  WHERE status = 'running'
2280
2299
  AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}
2281
- AND ${inFlightGraceSql()}`,
2300
+ AND ${inFlightGraceSql()}
2301
+ ORDER BY started_at ASC
2302
+ LIMIT ${REAP_ALL_STALE_BATCH_LIMIT + 1}`,
2282
2303
  args: [now, now, now],
2283
2304
  });
2305
+ const truncated = scanned.rows.length > REAP_ALL_STALE_BATCH_LIMIT;
2306
+ const stale = {
2307
+ rows: truncated
2308
+ ? scanned.rows.slice(0, REAP_ALL_STALE_BATCH_LIMIT)
2309
+ : scanned.rows,
2310
+ };
2284
2311
  for (const row of stale.rows) {
2285
2312
  const id = row.id;
2286
2313
  if (typeof id === "string") {
@@ -2299,13 +2326,19 @@ export async function reapAllStaleRuns() {
2299
2326
  // already found stale; on an idle app `hasRunningRuns` returns before any of
2300
2327
  // this, so the 20s fast sweep (agent-chat-plugin.ts) costs one probe.
2301
2328
  let reapedCount = 0;
2329
+ let failedCount = 0;
2302
2330
  for (const row of stale.rows) {
2303
2331
  const id = row.id;
2304
2332
  if (typeof id !== "string")
2305
2333
  continue;
2306
- const reaped = await reapSingleStaleRun(id).catch(() => false);
2307
- if (reaped)
2308
- reapedCount += 1;
2334
+ try {
2335
+ if (await reapSingleStaleRun(id))
2336
+ reapedCount += 1;
2337
+ }
2338
+ catch (error) {
2339
+ failedCount += 1;
2340
+ console.error(`[run-store] stale reap failed for run ${id}:`, error);
2341
+ }
2309
2342
  }
2310
2343
  for (const row of stale.rows) {
2311
2344
  const id = row.id;
@@ -2313,7 +2346,7 @@ export async function reapAllStaleRuns() {
2313
2346
  await safeAppendTerminalRunEvent(id, STALE_RUN_ERROR_EVENT, "reap-all-stale");
2314
2347
  }
2315
2348
  }
2316
- return reapedCount;
2349
+ return { reaped: reapedCount, failed: failedCount, truncated };
2317
2350
  }
2318
2351
  const RUN_OUTCOME_DAY_MS = 86_400_000;
2319
2352
  /**
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -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;
66
65
  ok: boolean;
66
+ error?: undefined;
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
- ok?: undefined;
52
51
  error: string;
52
+ ok?: undefined;
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
  }>>;
@@ -4907,7 +4907,10 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4907
4907
  //
4908
4908
  // Reported, never swallowed, and never fatal to the job sweep: a
4909
4909
  // reap failure must not take recurring jobs down with it, and
4910
- // `null` must stay distinguishable from "reaped nothing".
4910
+ // `null` must stay distinguishable from "reaped nothing". The
4911
+ // result carries `failed` and `truncated` for the same reason one
4912
+ // level down — a pass where every row threw, and a pass that hit
4913
+ // the batch cap, both used to be reportable as a clean sweep.
4911
4914
  const { reapAllStaleRuns } = await import("../agent/run-store.js");
4912
4915
  const staleRunsReaped = await reapAllStaleRuns().catch((error) => {
4913
4916
  console.error("[agent-chat] durable stale-run reap failed:", error);
@@ -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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.153.6",
3
+ "version": "0.153.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": {