@agent-native/core 0.161.2 → 0.161.6

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 (42) hide show
  1. package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
  2. package/corpus/templates/clips/actions/save-browser-transcript.ts +20 -4
  3. package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +167 -35
  4. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +8 -1
  5. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +39 -2
  6. package/corpus/templates/forms/server/lib/public-form-ssr.ts +3 -0
  7. package/corpus/templates/slides/actions/list-decks.ts +36 -1
  8. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -17
  9. package/corpus/templates/slides/app/context/DeckContext.tsx +17 -6
  10. package/dist/agent/engine/builder-engine.js +30 -15
  11. package/dist/agent/engine/types.d.ts +19 -0
  12. package/dist/agent/engine/types.js +3 -0
  13. package/dist/agent/production-agent.d.ts +56 -1
  14. package/dist/agent/production-agent.js +130 -3
  15. package/dist/agent/run-manager.d.ts +15 -4
  16. package/dist/agent/run-manager.js +26 -0
  17. package/dist/agent/run-store.d.ts +5 -5
  18. package/dist/agent/run-store.js +52 -15
  19. package/dist/agent/thread-data-builder.js +7 -0
  20. package/dist/agent/types.d.ts +10 -0
  21. package/dist/cli/code-agent-connector.js +6 -1
  22. package/dist/client/AssistantChat.d.ts +1 -0
  23. package/dist/client/AssistantChat.js +10 -1
  24. package/dist/client/MultiTabAssistantChat.js +33 -1
  25. package/dist/client/sse-event-processor.js +11 -7
  26. package/dist/client/use-chat-threads.js +9 -9
  27. package/dist/db/client.js +10 -2
  28. package/dist/db/create-get-db.js +42 -0
  29. package/dist/observability/routes.d.ts +3 -3
  30. package/dist/progress/routes.d.ts +1 -1
  31. package/dist/resources/handlers.d.ts +1 -1
  32. package/dist/secrets/routes.d.ts +9 -9
  33. package/dist/server/onboarding-html.js +22 -77
  34. package/dist/server/poll.d.ts +5 -5
  35. package/dist/server/poll.js +19 -26
  36. package/dist/server/realtime-token.d.ts +1 -1
  37. package/dist/server/transcribe-voice.d.ts +1 -1
  38. package/dist/shared/auth-copy.d.ts +7 -0
  39. package/dist/shared/auth-copy.js +77 -0
  40. package/dist/shared/mcp-embed-headers.js +8 -4
  41. package/dist/vite/client.js +94 -3
  42. package/package.json +1 -1
@@ -2357,6 +2357,8 @@ const RUN_OUTCOME_DAY_MS = 86_400_000;
2357
2357
  * every run id a user pasted into a bug report was gone before anyone looked.
2358
2358
  */
2359
2359
  const UNSUCCESSFUL_STATUS_SQL_LIST = `('errored', 'aborted', 'truncated')`;
2360
+ const RUN_OUTCOME_PRUNE_BATCH_LIMIT = 200;
2361
+ const RUN_OUTCOME_PRUNE_LOCK_KEY = "agent-native:run-outcome-prune";
2360
2362
  /**
2361
2363
  * Fold the terminal outcomes of the rows `cleanupOldRuns` is about to delete
2362
2364
  * into `agent_run_outcome_daily`, so success/failure RATES survive pruning even
@@ -2364,29 +2366,46 @@ const UNSUCCESSFUL_STATUS_SQL_LIST = `('errored', 'aborted', 'truncated')`;
2364
2366
  * covers exactly the unpruned ones, so a rate over any window is
2365
2367
  * `getRunOutcomeCounters()` plus the live rows — no gap, no double count.
2366
2368
  *
2367
- * The DELETE ... RETURNING is the claim: concurrent cleanup calls can both
2368
- * observe a row, but only the caller that deletes it receives it to roll up.
2369
- * Grouping the returned rows in TypeScript avoids dialect-specific date SQL.
2369
+ * Postgres callers take a transaction-scoped advisory lease before the claim.
2370
+ * The bounded DELETE ... RETURNING is still the source of truth for which rows
2371
+ * this invocation owns. Grouping the returned rows in TypeScript avoids
2372
+ * dialect-specific date SQL.
2370
2373
  * Counter upserts run in the same transaction as the delete; a failed upsert
2371
2374
  * rolls back the claim so the source rows remain available for a retry.
2372
2375
  */
2373
2376
  async function pruneAndRollUpPrunedRunOutcomes(client, cutoff, erroredCutoff) {
2374
2377
  const prune = async (tx) => {
2375
- await tx.execute({
2376
- sql: `DELETE FROM agent_run_events WHERE run_id IN (
2377
- SELECT id FROM agent_runs
2378
- WHERE (status = 'completed' AND completed_at < ?)
2379
- OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2380
- )`,
2381
- args: [cutoff, erroredCutoff],
2382
- });
2378
+ if (isPostgres()) {
2379
+ const lockResult = await tx.execute({
2380
+ sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
2381
+ args: [RUN_OUTCOME_PRUNE_LOCK_KEY],
2382
+ });
2383
+ const acquired = lockResult.rows[0]?.acquired;
2384
+ if (acquired !== true && acquired !== "t")
2385
+ return;
2386
+ }
2383
2387
  const { rows } = await tx.execute({
2384
2388
  sql: `DELETE FROM agent_runs
2385
- WHERE (status = 'completed' AND completed_at < ?)
2386
- OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2387
- RETURNING status, completed_at, terminal_reason`,
2389
+ WHERE id IN (
2390
+ SELECT id FROM agent_runs
2391
+ WHERE (status = 'completed' AND completed_at < ?)
2392
+ OR (status IN ${UNSUCCESSFUL_STATUS_SQL_LIST} AND completed_at < ?)
2393
+ ORDER BY completed_at ASC, id ASC
2394
+ LIMIT ${RUN_OUTCOME_PRUNE_BATCH_LIMIT}
2395
+ )
2396
+ RETURNING id, status, completed_at, terminal_reason`,
2388
2397
  args: [cutoff, erroredCutoff],
2389
2398
  });
2399
+ const runIds = rows
2400
+ .map((row) => row.id)
2401
+ .filter((id) => typeof id === "string");
2402
+ if (runIds.length > 0) {
2403
+ const placeholders = runIds.map(() => "?").join(", ");
2404
+ await tx.execute({
2405
+ sql: `DELETE FROM agent_run_events WHERE run_id IN (${placeholders})`,
2406
+ args: runIds,
2407
+ });
2408
+ }
2390
2409
  const groups = new Map();
2391
2410
  for (const row of rows) {
2392
2411
  const outcome = row;
@@ -2470,7 +2489,8 @@ export async function getRunOutcomeCounters(options) {
2470
2489
  * pruned at `olderThanMs`; errored/aborted/truncated runs are kept until
2471
2490
  * `erroredOlderThanMs` (a longer window, falling back to `olderThanMs`) so
2472
2491
  * their event log survives for cut-off pattern analysis via listErroredRuns. */
2473
- export async function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2492
+ let cleanupOldRunsInFlight;
2493
+ async function cleanupOldRunsInternal(olderThanMs, erroredOlderThanMs) {
2474
2494
  await ensureRunTables();
2475
2495
  const client = getDbExec();
2476
2496
  const cutoff = Date.now() - olderThanMs;
@@ -2567,6 +2587,23 @@ export async function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2567
2587
  // counting the same source rows.
2568
2588
  await pruneAndRollUpPrunedRunOutcomes(client, cutoff, erroredCutoff);
2569
2589
  }
2590
+ /**
2591
+ * Run cleanup is scheduled after every completed run, including completions
2592
+ * from several concurrent requests in one isolate. Share one sweep locally;
2593
+ * Postgres additionally serializes the durable prune across isolates.
2594
+ */
2595
+ export function cleanupOldRuns(olderThanMs, erroredOlderThanMs) {
2596
+ if (cleanupOldRunsInFlight)
2597
+ return cleanupOldRunsInFlight;
2598
+ const current = cleanupOldRunsInternal(olderThanMs, erroredOlderThanMs);
2599
+ let settled;
2600
+ settled = current.finally(() => {
2601
+ if (cleanupOldRunsInFlight === settled)
2602
+ cleanupOldRunsInFlight = undefined;
2603
+ });
2604
+ cleanupOldRunsInFlight = settled;
2605
+ return settled;
2606
+ }
2570
2607
  /**
2571
2608
  * List recent unsuccessful runs (errored, aborted, and truncated) for cut-off
2572
2609
  * pattern analysis. Read-only, bounded, and ordered newest-first. Surfaced via
@@ -9,6 +9,13 @@ function isInternalContinuationError(event) {
9
9
  const msg = event.error.toLowerCase();
10
10
  if (code === "builder_gateway_error")
11
11
  return false;
12
+ // An explicit `recoverable: false` outranks the code and message inference
13
+ // below, matching `isRecoverableContinuationError`. The background
14
+ // no-progress breaker stops a turn while PRESERVING the underlying transient
15
+ // code, so reading the code instead of the flag drops the one error the user
16
+ // was supposed to see out of the persisted turn.
17
+ if (event.recoverable === false)
18
+ return false;
12
19
  return (event.recoverable === true ||
13
20
  code === "builder_gateway_timeout" ||
14
21
  // Carries what `msg.includes("stream ended")` below used to: a
@@ -175,6 +175,16 @@ export interface AgentChatRequest {
175
175
  * boundary and refuses to chain past `MAX_BACKGROUND_RUN_CONTINUATIONS`.
176
176
  */
177
177
  continuationCount?: number;
178
+ /**
179
+ * Terminal error code the previous chunk failed with, plus how many chunks
180
+ * in a row have now ended on that same code having emitted no assistant
181
+ * text and no tool activity. Carried on the marker because each chunk is a
182
+ * separate invocation with no memory of the last one — without it the
183
+ * no-progress circuit breaker in `shouldChainBackgroundContinuation`
184
+ * cannot see a repeat at all.
185
+ */
186
+ noProgressErrorCode?: string;
187
+ noProgressCount?: number;
178
188
  /**
179
189
  * True when the dispatcher expects the self-POST to land in a real
180
190
  * Netlify `-background` function rather than the ~60s synchronous function.
@@ -163,6 +163,9 @@ class RemoteCodeAgentConnector {
163
163
  ok: false,
164
164
  error: err instanceof Error ? err.message : String(err),
165
165
  }));
166
+ if (result.ok === false) {
167
+ this.output.write(`Remote command ${command.id} failed: ${typeof result.error === "string" ? result.error : "Unknown connector error."}\n`);
168
+ }
166
169
  await this.postCommandResult(command, result);
167
170
  }
168
171
  }
@@ -334,7 +337,9 @@ class RemoteCodeAgentConnector {
334
337
  commandId: command.id,
335
338
  deviceId: this.config.deviceId,
336
339
  relayUrl: this.relayUrl,
337
- ...(portalWorkspace ? { remoteRunId: run.id } : {}),
340
+ ...(portalWorkspace && requestedRunId
341
+ ? { remoteRunId: requestedRunId }
342
+ : {}),
338
343
  },
339
344
  ...(portalWorkspace
340
345
  ? {
@@ -14,6 +14,7 @@ import { type ContentPart } from "./sse-event-processor.js";
14
14
  import type { ChatThreadScope, ChatThreadSnapshot } from "./use-chat-threads.js";
15
15
  export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
16
16
  export { displayableUserMessageText } from "./chat/message-components.js";
17
+ export declare function shouldSuppressUnauthenticatedDesktopThreadRestore(surface: AgentChatSurfaceKind, status: number): boolean;
17
18
  type AssistantUiMessageResourceShape = {
18
19
  id: string;
19
20
  content: readonly unknown[];
@@ -44,6 +44,11 @@ import { useRunStuckDetection } from "./use-run-stuck-detection.js";
44
44
  import { cn } from "./utils.js";
45
45
  export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
46
46
  export { displayableUserMessageText } from "./chat/message-components.js";
47
+ // Desktop chat mounts beside the parent identity gate, so an unauthenticated
48
+ // relay is an expected empty state until that gate establishes a session.
49
+ export function shouldSuppressUnauthenticatedDesktopThreadRestore(surface, status) {
50
+ return surface === "desktop" && (status === 401 || status === 403);
51
+ }
47
52
  const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
48
53
  export function assistantUiMessageListStructureKey(messages) {
49
54
  return JSON.stringify(messages.map((message) => [
@@ -2654,7 +2659,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2654
2659
  const res = await fetch(`${apiUrl}/threads/${encodeURIComponent(threadId)}`);
2655
2660
  if (!res.ok) {
2656
2661
  if (!cancelled) {
2657
- setThreadRestoreError(res.status === 404 ? "not-found" : "unavailable");
2662
+ setThreadRestoreError(shouldSuppressUnauthenticatedDesktopThreadRestore(agentChatSurface, res.status)
2663
+ ? null
2664
+ : res.status === 404
2665
+ ? "not-found"
2666
+ : "unavailable");
2658
2667
  }
2659
2668
  return;
2660
2669
  }
@@ -789,7 +789,11 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
789
789
  catch { }
790
790
  }, [subAgentNames, SUB_AGENT_NAMES_KEY]);
791
791
  // Open tabs — persisted to localStorage so they survive refresh.
792
- const OPEN_TABS_KEY = `agent-chat-open-tabs${keyPrefix}`;
792
+ // Per-scope, for the same reason the active thread is: the tab list must
793
+ // follow the resource in view, so one resource's tabs never stay mounted
794
+ // (and rebroadcasting their run state) while another resource is open.
795
+ const scopeKeyPart = scope ? `:scope:${scope.type}:${scope.id}` : "";
796
+ const OPEN_TABS_KEY = `agent-chat-open-tabs${keyPrefix}${scopeKeyPart}`;
793
797
  const [openTabIds, setOpenTabIds] = useState(() => {
794
798
  if (!restoreActiveThread && activeThreadId) {
795
799
  for (const id of [activeThreadId])
@@ -814,7 +818,35 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
814
818
  const openTabIdsRef = useRef(openTabIds);
815
819
  openTabIdsRef.current = openTabIds;
816
820
  const initializedRef = useRef(false);
821
+ // Rehydrate open tabs when the scope flips. Read the new key before the
822
+ // persistence effect can write the current (now-wrong) tab list under it.
817
823
  const openTabsKeyRef = useRef(OPEN_TABS_KEY);
824
+ useEffect(() => {
825
+ if (openTabsKeyRef.current === OPEN_TABS_KEY)
826
+ return;
827
+ openTabsKeyRef.current = OPEN_TABS_KEY;
828
+ initializedRef.current = false;
829
+ if (!restoreActiveThread) {
830
+ setOpenTabIds(activeThreadId ? [activeThreadId] : []);
831
+ return;
832
+ }
833
+ try {
834
+ const saved = localStorage.getItem(OPEN_TABS_KEY);
835
+ if (saved) {
836
+ const parsed = JSON.parse(saved);
837
+ if (Array.isArray(parsed)) {
838
+ for (const id of parsed)
839
+ mountedTabsRef.current.add(id);
840
+ setOpenTabIds(parsed);
841
+ return;
842
+ }
843
+ }
844
+ }
845
+ catch {
846
+ // coercion-ok: malformed persisted tab data is an absent tab list.
847
+ }
848
+ setOpenTabIds([]);
849
+ }, [OPEN_TABS_KEY, activeThreadId, restoreActiveThread]);
818
850
  useBrowserLayoutEffect(() => {
819
851
  const nextScope = scope;
820
852
  if (!nextScope)
@@ -473,6 +473,17 @@ async function readChunkWithProgressTimeout(reader, lastMeaningfulEventAt, noPro
473
473
  function isAutoRecoverableError(ev, errMsg) {
474
474
  const code = String(ev.errorCode ?? "").toLowerCase();
475
475
  const msg = errMsg.toLowerCase();
476
+ // An explicit `recoverable: false` outranks EVERY inference below — the code
477
+ // list as well as the message sniff — matching the server's own precedence in
478
+ // `isRecoverableContinuationError`. The repeat guards stop a turn with a
479
+ // message that names the looping tool, so a stop on
480
+ // `list-workspace-connections` matched the "connection" sniff and
481
+ // auto-continued the very loop it was emitted to break; the background
482
+ // no-progress breaker stops one while PRESERVING the underlying transient
483
+ // code (so the failure stays diagnosable), so reading the code instead of the
484
+ // flag re-POSTs the exact chain the server just refused to continue.
485
+ if (ev.recoverable === false)
486
+ return false;
476
487
  if (code === "context_length_exceeded" ||
477
488
  code === "input_too_long" ||
478
489
  code.startsWith("credits-limit") ||
@@ -548,13 +559,6 @@ function isAutoRecoverableError(ev, errMsg) {
548
559
  }
549
560
  if (ev.recoverable === true)
550
561
  return true;
551
- // An explicit flag outranks the message sniff below, which exists only for
552
- // events that carry no flag at all. The repeat guards stop a turn with
553
- // `recoverable: false` and a message that names the looping tool, so a stop
554
- // on `list-workspace-connections` matched the "connection" sniff and
555
- // auto-continued the very loop it was emitted to break.
556
- if (ev.recoverable === false)
557
- return false;
558
562
  if (msg.includes("daily gateway request cap"))
559
563
  return false;
560
564
  // The engine's structural verdict, checked after every terminal code above so
@@ -467,9 +467,9 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
467
467
  // it; the server hasn't seen it yet because there's no POST anymore,
468
468
  // the row gets written when the user sends a message.
469
469
  // - savedId is set but not on the current page → look it up directly. A
470
- // found thread stays active. An unavailable lookup keeps the saved id so
471
- // the detail surface can preserve cached state and offer recovery instead
472
- // of replacing a shared/reopened conversation with a blank local tab.
470
+ // found thread stays active. An unavailable lookup keeps the saved id for
471
+ // list-only readers and explicit routes, while a normal home surface drops
472
+ // a dead local pointer instead of opening with a restore error.
473
473
  // - No savedId → synthesize a fresh local id (no POST; server creates the
474
474
  // row on first message). The server may contain chats from another
475
475
  // branch, preview, or project that shares the same user/database, so
@@ -511,12 +511,12 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
511
511
  const restoredIsUnavailable = restoredThread === null && lookupRestored && !restoredOnPage;
512
512
  const restoredBelongsElsewhere = Boolean(restoredThread &&
513
513
  !threadCanStayVisibleInScope(restoredThread.scope ?? null, scopeRef.current));
514
- // Keep the saved id when the direct lookup says 404. AssistantChat owns
515
- // the detail restore and can show a retryable error while preserving any
516
- // cached transcript or composer draft. Replacing the id here silently
517
- // turns a shared/reopened conversation into a new blank chat after the
518
- // slower lookup finishes.
519
- const restoredNeedsReplacement = restoredBelongsElsewhere;
514
+ // A missing saved id is stale local UI state on a normal home surface,
515
+ // not a reason to show an error above a fresh composer. Preserve it for
516
+ // explicit routes and list-only readers, where the caller still owns
517
+ // recovery for a deliberately selected thread.
518
+ const restoredNeedsReplacement = restoredBelongsElsewhere ||
519
+ (restoredIsUnavailable && autoCreate && !routeControlsActiveThread);
520
520
  if (restoredNeedsReplacement)
521
521
  setActiveThreadId(null);
522
522
  const savedId = restoredNeedsReplacement ? null : restoredId;
package/dist/db/client.js CHANGED
@@ -1258,7 +1258,12 @@ async function createDbExecInternal(config = {}, trackSingletonResources = false
1258
1258
  const { rawSql, args } = sqlAndArgs(sql);
1259
1259
  const { timeoutMs } = dbExecQueryBudget(sql);
1260
1260
  const pgSql = sqliteToPostgresParams(rawSql);
1261
- const result = await withDbTimeout("query", () => client.query(pgSql, args), timeoutOverrideMs ?? timeoutMs);
1261
+ // Neon only accepts multiple SQL commands through its simple protocol;
1262
+ // the transaction start has no parameters, so use that overload.
1263
+ const runQuery = () => args.length === 0 && rawSql.includes(";")
1264
+ ? client.query(pgSql)
1265
+ : client.query(pgSql, args);
1266
+ const result = await withDbTimeout("query", () => runQuery(), timeoutOverrideMs ?? timeoutMs);
1262
1267
  return {
1263
1268
  rows: result.rows,
1264
1269
  rowsAffected: result.rowCount ?? 0,
@@ -1404,7 +1409,10 @@ async function createDbExecInternal(config = {}, trackSingletonResources = false
1404
1409
  },
1405
1410
  };
1406
1411
  try {
1407
- await queryNeonClient(client, "BEGIN");
1412
+ // Send the transaction start and idle reaper together. Neon
1413
+ // transaction pooling can ignore startup parameters, and a
1414
+ // worker can die between separate BEGIN and SET LOCAL calls.
1415
+ await queryNeonClient(client, "BEGIN; SET LOCAL idle_in_transaction_session_timeout = 30000");
1408
1416
  const result = await fn(tx);
1409
1417
  await queryNeonClient(client, "COMMIT");
1410
1418
  releaseClient();
@@ -40,6 +40,43 @@ function getNeonServerlessDrizzle() {
40
40
  export function isSqlRead(sql) {
41
41
  return /^\s*(SELECT|WITH\s)/i.test(sql);
42
42
  }
43
+ const NEON_IDLE_IN_TRANSACTION_TIMEOUT_SQL = "SET LOCAL idle_in_transaction_session_timeout = 30000";
44
+ function queryText(sql) {
45
+ if (typeof sql === "string")
46
+ return sql;
47
+ if (sql && typeof sql === "object" && "text" in sql) {
48
+ const text = sql.text;
49
+ return typeof text === "string" ? text : "";
50
+ }
51
+ return "";
52
+ }
53
+ function isBeginQuery(sql) {
54
+ return /^\s*BEGIN(?:\s|$)/i.test(queryText(sql));
55
+ }
56
+ /**
57
+ * Drizzle sends BEGIN through the client returned by pool.connect(), so a
58
+ * pool startup parameter alone is not enough protection when Neon routes the
59
+ * connection through a transaction pooler. Put the idle timeout in the same
60
+ * simple-protocol message as BEGIN; a worker killed before its next query
61
+ * still leaves a backend that will reap itself.
62
+ */
63
+ function guardNeonTransactionClient(client) {
64
+ return new Proxy(client, {
65
+ get(target, prop) {
66
+ if (prop !== "query") {
67
+ const value = target[prop];
68
+ return typeof value === "function" ? value.bind(target) : value;
69
+ }
70
+ return (...args) => {
71
+ const sql = args[0];
72
+ if (!isBeginQuery(sql))
73
+ return target.query(...args);
74
+ const text = queryText(sql).replace(/;\s*$/, "");
75
+ return target.query(`${text}; ${NEON_IDLE_IN_TRANSACTION_TIMEOUT_SQL}`);
76
+ };
77
+ },
78
+ });
79
+ }
43
80
  /**
44
81
  * Wraps a @neondatabase/serverless Pool so every query goes through
45
82
  * the same withDbTimeout + retryOnConnectionError resilience that the
@@ -129,6 +166,11 @@ export function buildResilientNeonPool(pool) {
129
166
  get(target, prop) {
130
167
  if (prop === "query")
131
168
  return resilientQuery;
169
+ if (prop === "connect") {
170
+ return (...args) => target
171
+ .connect(...args)
172
+ .then((client) => guardNeonTransactionClient(client));
173
+ }
132
174
  const val = target[prop];
133
175
  return typeof val === "function" ? val.bind(target) : val;
134
176
  },
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
42
42
  avgEvalScore: number;
43
43
  } | {
44
44
  error?: undefined;
45
+ ok?: undefined;
45
46
  summary: import("./types.js").TraceSummary;
46
47
  spans: import("./types.js").TraceSpan[];
47
48
  id?: undefined;
48
- ok?: undefined;
49
49
  } | {
50
50
  error?: undefined;
51
+ ok?: undefined;
51
52
  summary?: undefined;
52
53
  spans?: undefined;
53
54
  id: string;
54
- ok?: undefined;
55
55
  } | {
56
+ ok?: undefined;
56
57
  summary?: undefined;
57
58
  spans?: undefined;
58
59
  id?: undefined;
59
60
  error: any;
60
- ok?: undefined;
61
61
  } | {
62
62
  error?: undefined;
63
63
  summary?: undefined;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
@@ -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
- status?: undefined;
38
37
  ok?: undefined;
38
+ status?: undefined;
39
39
  } | {
40
+ error?: undefined;
40
41
  ok: boolean;
41
42
  status: string;
42
- error?: undefined;
43
43
  } | {
44
+ ok?: undefined;
44
45
  error: string;
45
46
  removed?: undefined;
46
- ok?: undefined;
47
47
  } | {
48
+ error?: undefined;
48
49
  ok: boolean;
49
50
  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;
57
58
  error: string;
58
59
  note?: undefined;
59
- ok?: undefined;
60
60
  } | {
61
+ error?: undefined;
61
62
  ok: boolean;
62
63
  note?: undefined;
63
- error?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  ok: boolean;
66
67
  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;
98
99
  ok: boolean;
99
100
  key: string;
100
- error?: undefined;
101
101
  } | {
102
+ error?: undefined;
102
103
  ok: boolean;
103
104
  removed: boolean;
104
- error?: undefined;
105
105
  }>>;