@agent-native/core 0.159.5 → 0.160.0

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 (76) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/assets/server/lib/generation.ts +10 -6
  3. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +18 -4
  4. package/corpus/templates/calendar/actions/create-event.ts +4 -2
  5. package/corpus/templates/calendar/actions/event-action-helpers.ts +56 -9
  6. package/corpus/templates/calendar/actions/update-event.ts +1 -0
  7. package/corpus/templates/calendar/agent-native.config.ts +1 -0
  8. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +8 -2
  9. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +102 -68
  10. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +150 -49
  11. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +57 -23
  12. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +4 -1
  13. package/corpus/templates/calendar/app/components/calendar/TimezoneSwitchDialog.tsx +70 -0
  14. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +127 -88
  15. package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +59 -25
  16. package/corpus/templates/calendar/app/hooks/use-event-drag.ts +55 -18
  17. package/corpus/templates/calendar/app/i18n-data.ts +60 -0
  18. package/corpus/templates/calendar/app/lib/all-day-layout.ts +16 -8
  19. package/corpus/templates/calendar/app/lib/calendar-drafts.ts +56 -0
  20. package/corpus/templates/calendar/app/lib/calendar-timezone.ts +351 -0
  21. package/corpus/templates/calendar/app/lib/event-layout.ts +14 -8
  22. package/corpus/templates/calendar/app/lib/out-of-office.ts +29 -61
  23. package/corpus/templates/calendar/app/lib/working-location.ts +34 -2
  24. package/corpus/templates/calendar/app/pages/CalendarView.tsx +317 -139
  25. package/corpus/templates/calendar/changelog/2026-08-13-calendar-stays-pinned-to-the-saved-timezone-and-asks-before-.md +6 -0
  26. package/corpus/templates/calendar/changelog/2026-08-13-working-locations-can-be-added-from-calendar-days-with-a-ful.md +6 -0
  27. package/corpus/templates/calendar/changelog/2026-08-14-choosing-office-or-other-when-adding-a-working-location-creates.md +6 -0
  28. package/corpus/templates/calendar/changelog/2026-08-14-creating-an-other-working-location-now-keeps-the-custom-name.md +6 -0
  29. package/corpus/templates/calendar/changelog/2026-08-14-timed-working-locations-keep-home-office-or-custom-titles.md +6 -0
  30. package/corpus/templates/calendar/changelog/2026-08-14-timed-working-locations-now-keep-a-home-office-or-custom-title.md +6 -0
  31. package/corpus/templates/calendar/changelog/2026-08-14-turning-a-midnight-ending-working-location-back-to-all-day.md +6 -0
  32. package/corpus/templates/calendar/server/lib/google-calendar.ts +22 -1
  33. package/dist/agent/engine/builder-engine.d.ts +16 -2
  34. package/dist/agent/engine/builder-engine.js +178 -115
  35. package/dist/agent/engine/builtin.js +13 -4
  36. package/dist/agent/engine/credential-errors.d.ts +23 -0
  37. package/dist/agent/engine/credential-errors.js +19 -0
  38. package/dist/agent/engine/error-detail.d.ts +15 -0
  39. package/dist/agent/engine/error-detail.js +29 -0
  40. package/dist/agent/engine/registry.d.ts +14 -3
  41. package/dist/agent/engine/registry.js +106 -61
  42. package/dist/agent/engine/types.d.ts +16 -0
  43. package/dist/agent/engine/types.js +9 -0
  44. package/dist/agent/production-agent.d.ts +7 -0
  45. package/dist/agent/production-agent.js +58 -20
  46. package/dist/agent/run-manager.js +30 -0
  47. package/dist/agent/thread-data-builder.js +4 -0
  48. package/dist/agent/types.d.ts +9 -0
  49. package/dist/client/agent-chat-adapter.js +46 -15
  50. package/dist/client/error-format.js +25 -2
  51. package/dist/client/sse-event-processor.d.ts +2 -0
  52. package/dist/client/sse-event-processor.js +12 -1
  53. package/dist/collab/awareness.d.ts +2 -2
  54. package/dist/collab/struct-routes.d.ts +1 -1
  55. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  56. package/dist/jobs/background-automation-runner.js +9 -15
  57. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  58. package/dist/notifications/routes.d.ts +6 -6
  59. package/dist/observability/routes.d.ts +5 -5
  60. package/dist/resources/handlers.d.ts +1 -1
  61. package/dist/secrets/register-framework-secrets.js +25 -0
  62. package/dist/secrets/routes.d.ts +9 -9
  63. package/dist/server/agent-chat-plugin.js +9 -2
  64. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  65. package/dist/server/credential-provider.d.ts +80 -0
  66. package/dist/server/credential-provider.js +168 -1
  67. package/dist/server/google-realtime-session.d.ts +1 -1
  68. package/dist/server/google-realtime-session.js +6 -6
  69. package/dist/server/index.d.ts +1 -1
  70. package/dist/server/index.js +6 -1
  71. package/dist/server/realtime-token.d.ts +1 -1
  72. package/dist/server/realtime-voice.js +12 -6
  73. package/dist/server/transcribe-voice.d.ts +1 -1
  74. package/dist/server/transcribe-voice.js +18 -16
  75. package/dist/transcription/builder-transcription.js +8 -4
  76. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { redactSensitiveFields, sanitizeToolErrorText, sanitizeToolErrorValue, }
5
5
  import { TOOL_SEARCH_ACTION_NAME } from "../agent/tool-search.js";
6
6
  import { parseAcceptLanguage } from "../localization/server.js";
7
7
  import { getSession } from "./auth.js";
8
- import { getBuilderGatewayBaseUrl, resolveBuilderCredentials, resolveSecret, } from "./credential-provider.js";
8
+ import { gatewayLaneUnavailableMessage, getBuilderGatewayBaseUrl, resolveBuilderGatewayCredentials, resolveSecret, } from "./credential-provider.js";
9
9
  import { getH3App } from "./framework-request-handler.js";
10
10
  import { runWithRequestContext } from "./request-context.js";
11
11
  import { isSameOriginRequest } from "./request-origin.js";
@@ -379,7 +379,7 @@ function createSessionHandler(tools, options) {
379
379
  ? { browserTabId: auth.browserTabId }
380
380
  : undefined,
381
381
  }, async () => {
382
- const builderCredentials = await resolveBuilderCredentials();
382
+ const builderCredentials = await resolveBuilderGatewayCredentials();
383
383
  const builderConfigured = Boolean(builderCredentials.privateKey?.trim() &&
384
384
  builderCredentials.publicKey?.trim());
385
385
  const apiKey = builderConfigured
@@ -388,7 +388,7 @@ function createSessionHandler(tools, options) {
388
388
  if (!builderConfigured && !apiKey) {
389
389
  setResponseStatus(event, 409);
390
390
  return {
391
- error: "Connect Builder (free tier available) or configure an OpenAI API key to use realtime voice.",
391
+ error: gatewayLaneUnavailableMessage("Connect Builder (free tier available) or configure an OpenAI API key to use realtime voice."),
392
392
  code: "realtime_voice_setup_required",
393
393
  };
394
394
  }
@@ -466,22 +466,28 @@ function createSessionHandler(tools, options) {
466
466
  setResponseStatus(event, 502);
467
467
  return {
468
468
  error: builderConfigured
469
- ? "Could not reach the Builder realtime voice gateway"
469
+ ? gatewayLaneUnavailableMessage("Could not reach the Builder realtime voice gateway")
470
470
  : "Could not reach the OpenAI Realtime API",
471
471
  };
472
472
  }
473
473
  if (!upstream.ok) {
474
474
  const detail = await safeOpenAiErrorDetail(upstream, builderConfigured ? builderCredentials.privateKey : apiKey);
475
475
  setResponseStatus(event, builderConfigured ? upstream.status : 502);
476
+ const rejection = `${builderConfigured ? "Builder" : "OpenAI"} rejected the realtime session (${upstream.status})${detail ? `: ${detail}` : ""}`;
476
477
  return {
477
- error: `${builderConfigured ? "Builder" : "OpenAI"} rejected the realtime session (${upstream.status})${detail ? `: ${detail}` : ""}`,
478
+ error: builderConfigured
479
+ ? gatewayLaneUnavailableMessage(rejection)
480
+ : rejection,
478
481
  };
479
482
  }
480
483
  const answerSdp = await upstream.text().catch(() => "");
481
484
  if (!answerSdp.trim()) {
482
485
  setResponseStatus(event, 502);
486
+ const emptyAnswer = `${builderConfigured ? "Builder" : "OpenAI"} returned an empty realtime session answer`;
483
487
  return {
484
- error: `${builderConfigured ? "Builder" : "OpenAI"} returned an empty realtime session answer`,
488
+ error: builderConfigured
489
+ ? gatewayLaneUnavailableMessage(emptyAnswer)
490
+ : emptyAnswer,
485
491
  };
486
492
  }
487
493
  setResponseStatus(event, upstream.status);
@@ -20,6 +20,6 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- error?: undefined;
24
23
  text: string;
24
+ error?: undefined;
25
25
  }>>;
@@ -23,7 +23,7 @@ import { getOrgContext } from "../org/context.js";
23
23
  import { transcribeWithBuilder } from "../transcription/builder-transcription.js";
24
24
  import { applyVoiceContextReplacements, buildVoiceGuidanceBlock, parseVoiceContextPack, voiceContextTermsOnly, } from "../voice/index.js";
25
25
  import { getSession } from "./auth.js";
26
- import { resolveHasBuilderPrivateKey, resolveSecret, } from "./credential-provider.js";
26
+ import { gatewayLaneUnavailableMessage, resolveHasBuilderGatewayCredential, resolveSecret, } from "./credential-provider.js";
27
27
  import { runWithRequestContext } from "./request-context.js";
28
28
  import { isSameOriginRequest } from "./request-origin.js";
29
29
  const WHISPER_URL = "https://api.openai.com/v1/audio/transcriptions";
@@ -149,7 +149,7 @@ export function createTranscribeVoiceHandler() {
149
149
  const withRequestContext = async (fn) => requestContext.userEmail
150
150
  ? runWithRequestContext(requestContext, fn)
151
151
  : fn();
152
- const hasBuilderPrivateKey = async () => withRequestContext(() => resolveHasBuilderPrivateKey());
152
+ const hasBuilderCredential = async () => withRequestContext(() => resolveHasBuilderGatewayCredential());
153
153
  const transcribeWithBuilderForRequest = (opts) => withRequestContext(() => transcribeWithBuilder(opts));
154
154
  const sessionId = session?.email ?? "local";
155
155
  let providerPref;
@@ -212,7 +212,7 @@ export function createTranscribeVoiceHandler() {
212
212
  instructions: voiceGuidance,
213
213
  contextPack: voiceContext,
214
214
  providerPref,
215
- hasBuilderPrivateKey,
215
+ hasBuilderCredential,
216
216
  withRequestContext,
217
217
  resolveApiKey,
218
218
  clientAbortSignal: clientAbort,
@@ -266,10 +266,10 @@ export function createTranscribeVoiceHandler() {
266
266
  const label = providerPref === "builder-gemini"
267
267
  ? "Builder Gemini Flash-Lite"
268
268
  : "Builder";
269
- if (!(await hasBuilderPrivateKey())) {
269
+ if (!(await hasBuilderCredential())) {
270
270
  setResponseStatus(event, 400);
271
271
  return {
272
- error: `${label} is selected but Builder.io is not connected. Connect Builder.io (free tier available) in Settings, or change the provider preference.`,
272
+ error: gatewayLaneUnavailableMessage(`${label} is selected but Builder.io is not connected. Connect Builder.io (free tier available) in Settings, or change the provider preference.`),
273
273
  };
274
274
  }
275
275
  try {
@@ -288,10 +288,12 @@ export function createTranscribeVoiceHandler() {
288
288
  const message = err?.message ?? String(err);
289
289
  if (message.includes("credits exhausted")) {
290
290
  setResponseStatus(event, 402);
291
- return { error: message };
291
+ return { error: gatewayLaneUnavailableMessage(message) };
292
292
  }
293
293
  setResponseStatus(event, 502);
294
- return { error: `${label} transcription failed: ${message}` };
294
+ return {
295
+ error: gatewayLaneUnavailableMessage(`${label} transcription failed: ${message}`),
296
+ };
295
297
  }
296
298
  }
297
299
  if (providerPref === "groq") {
@@ -322,7 +324,7 @@ export function createTranscribeVoiceHandler() {
322
324
  // ── Builder Gemini Flash-Lite path ─────────────────────────────────
323
325
  // First-priority in auto mode when Builder is connected. This lets users
324
326
  // try Gemini 3.1 Flash-Lite without bringing their own Google key.
325
- if (providerPref !== "openai" && (await hasBuilderPrivateKey())) {
327
+ if (providerPref !== "openai" && (await hasBuilderCredential())) {
326
328
  try {
327
329
  const result = await transcribeWithBuilderForRequest({
328
330
  audioBytes,
@@ -339,7 +341,7 @@ export function createTranscribeVoiceHandler() {
339
341
  // a specific upgrade prompt.
340
342
  if (message.includes("credits exhausted")) {
341
343
  setResponseStatus(event, 402);
342
- return { error: message };
344
+ return { error: gatewayLaneUnavailableMessage(message) };
343
345
  }
344
346
  builderError = message;
345
347
  }
@@ -401,9 +403,9 @@ export function createTranscribeVoiceHandler() {
401
403
  if (!provider) {
402
404
  setResponseStatus(event, builderError ? 502 : 400);
403
405
  return {
404
- error: builderError
406
+ error: gatewayLaneUnavailableMessage(builderError
405
407
  ? `Builder transcription failed: ${builderError}. Add GEMINI_API_KEY, GROQ_API_KEY, or OPENAI_API_KEY in Settings → API Keys to enable a fallback provider.`
406
- : "No voice transcription provider configured. Connect Builder.io (free tier available) or add GEMINI_API_KEY / GROQ_API_KEY / OPENAI_API_KEY in Settings → API Keys.",
408
+ : "No voice transcription provider configured. Connect Builder.io (free tier available) or add GEMINI_API_KEY / GROQ_API_KEY / OPENAI_API_KEY in Settings → API Keys."),
407
409
  };
408
410
  }
409
411
  return await callWhisperCompat({
@@ -477,7 +479,7 @@ async function callWhisperCompat({ event, provider, audioBytes, mime, language,
477
479
  clearTimeout(timeout);
478
480
  }
479
481
  }
480
- async function cleanupTranscriptText({ event, text, instructions, contextPack, providerPref, hasBuilderPrivateKey, withRequestContext, resolveApiKey, clientAbortSignal, }) {
482
+ async function cleanupTranscriptText({ event, text, instructions, contextPack, providerPref, hasBuilderCredential, withRequestContext, resolveApiKey, clientAbortSignal, }) {
481
483
  const original = text.trim();
482
484
  if (!original)
483
485
  return { text: "" };
@@ -486,10 +488,10 @@ async function cleanupTranscriptText({ event, text, instructions, contextPack, p
486
488
  return { text: finalizeText(original) };
487
489
  }
488
490
  if (providerPref === "builder" || providerPref === "builder-gemini") {
489
- if (!(await hasBuilderPrivateKey())) {
491
+ if (!(await hasBuilderCredential())) {
490
492
  setResponseStatus(event, 400);
491
493
  return {
492
- error: "Builder.io cleanup is selected but Builder.io is not connected. Connect Builder.io (free tier available) in Settings, or change the provider preference.",
494
+ error: gatewayLaneUnavailableMessage("Builder.io cleanup is selected but Builder.io is not connected. Connect Builder.io (free tier available) in Settings, or change the provider preference."),
493
495
  };
494
496
  }
495
497
  try {
@@ -506,7 +508,7 @@ async function cleanupTranscriptText({ event, text, instructions, contextPack, p
506
508
  catch (err) {
507
509
  setResponseStatus(event, 502);
508
510
  return {
509
- error: `Builder.io cleanup failed: ${err?.message ?? String(err)}`,
511
+ error: gatewayLaneUnavailableMessage(`Builder.io cleanup failed: ${err?.message ?? String(err)}`),
510
512
  };
511
513
  }
512
514
  }
@@ -560,7 +562,7 @@ async function cleanupTranscriptText({ event, text, instructions, contextPack, p
560
562
  };
561
563
  }
562
564
  }
563
- if (await hasBuilderPrivateKey()) {
565
+ if (await hasBuilderCredential()) {
564
566
  try {
565
567
  const cleaned = await withRequestContext(() => cleanupWithBuilder({ text: original, instructions, clientAbortSignal }));
566
568
  if (cleaned)
@@ -1,4 +1,4 @@
1
- import { resolveBuilderAuthHeader, getBuilderProxyOrigin, } from "../server/credential-provider.js";
1
+ import { resolveBuilderGatewayAuth, getBuilderProxyOrigin, } from "../server/credential-provider.js";
2
2
  function describeError(err) {
3
3
  if (!(err instanceof Error))
4
4
  return String(err);
@@ -7,8 +7,8 @@ function describeError(err) {
7
7
  return `${err.name}: ${err.message}${causeText}`;
8
8
  }
9
9
  export async function transcribeWithBuilder(opts) {
10
- const authHeader = await resolveBuilderAuthHeader();
11
- if (!authHeader) {
10
+ const auth = await resolveBuilderGatewayAuth();
11
+ if (!auth) {
12
12
  throw new Error("Builder private key not configured. Connect your Builder.io account (free tier available) in Settings.");
13
13
  }
14
14
  const params = new URLSearchParams();
@@ -39,8 +39,12 @@ export async function transcribeWithBuilder(opts) {
39
39
  res = await fetch(url, {
40
40
  method: "POST",
41
41
  headers: {
42
- Authorization: authHeader,
42
+ Authorization: auth.authorization,
43
43
  "Content-Type": "application/octet-stream",
44
+ // A gateway token without a space id is rejected before any route
45
+ // policy is consulted, so the space id travels with every call.
46
+ ...(auth.spaceId ? { "x-builder-api-key": auth.spaceId } : {}),
47
+ ...(auth.userId ? { "x-builder-user-id": auth.userId } : {}),
44
48
  },
45
49
  body,
46
50
  signal: controller.signal,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.159.5",
3
+ "version": "0.160.0",
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": {