@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/assets/server/lib/generation.ts +10 -6
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +18 -4
- package/corpus/templates/calendar/actions/create-event.ts +4 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +56 -9
- package/corpus/templates/calendar/actions/update-event.ts +1 -0
- package/corpus/templates/calendar/agent-native.config.ts +1 -0
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +8 -2
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +102 -68
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +150 -49
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +57 -23
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +4 -1
- package/corpus/templates/calendar/app/components/calendar/TimezoneSwitchDialog.tsx +70 -0
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +127 -88
- package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +59 -25
- package/corpus/templates/calendar/app/hooks/use-event-drag.ts +55 -18
- package/corpus/templates/calendar/app/i18n-data.ts +60 -0
- package/corpus/templates/calendar/app/lib/all-day-layout.ts +16 -8
- package/corpus/templates/calendar/app/lib/calendar-drafts.ts +56 -0
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +351 -0
- package/corpus/templates/calendar/app/lib/event-layout.ts +14 -8
- package/corpus/templates/calendar/app/lib/out-of-office.ts +29 -61
- package/corpus/templates/calendar/app/lib/working-location.ts +34 -2
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +317 -139
- package/corpus/templates/calendar/changelog/2026-08-13-calendar-stays-pinned-to-the-saved-timezone-and-asks-before-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-13-working-locations-can-be-added-from-calendar-days-with-a-ful.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-14-choosing-office-or-other-when-adding-a-working-location-creates.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-14-creating-an-other-working-location-now-keeps-the-custom-name.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-14-timed-working-locations-keep-home-office-or-custom-titles.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-14-timed-working-locations-now-keep-a-home-office-or-custom-title.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-14-turning-a-midnight-ending-working-location-back-to-all-day.md +6 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +22 -1
- package/dist/agent/engine/builder-engine.d.ts +16 -2
- package/dist/agent/engine/builder-engine.js +178 -115
- package/dist/agent/engine/builtin.js +13 -4
- package/dist/agent/engine/credential-errors.d.ts +23 -0
- package/dist/agent/engine/credential-errors.js +19 -0
- package/dist/agent/engine/error-detail.d.ts +15 -0
- package/dist/agent/engine/error-detail.js +29 -0
- package/dist/agent/engine/registry.d.ts +14 -3
- package/dist/agent/engine/registry.js +106 -61
- package/dist/agent/engine/types.d.ts +16 -0
- package/dist/agent/engine/types.js +9 -0
- package/dist/agent/production-agent.d.ts +7 -0
- package/dist/agent/production-agent.js +58 -20
- package/dist/agent/run-manager.js +30 -0
- package/dist/agent/thread-data-builder.js +4 -0
- package/dist/agent/types.d.ts +9 -0
- package/dist/client/agent-chat-adapter.js +46 -15
- package/dist/client/error-format.js +25 -2
- package/dist/client/sse-event-processor.d.ts +2 -0
- package/dist/client/sse-event-processor.js +12 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/background-automation-runner.js +9 -15
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.js +9 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/credential-provider.d.ts +80 -0
- package/dist/server/credential-provider.js +168 -1
- package/dist/server/google-realtime-session.d.ts +1 -1
- package/dist/server/google-realtime-session.js +6 -6
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/realtime-voice.js +12 -6
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/transcribe-voice.js +18 -16
- package/dist/transcription/builder-transcription.js +8 -4
- 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,
|
|
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
|
|
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:
|
|
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:
|
|
488
|
+
error: builderConfigured
|
|
489
|
+
? gatewayLaneUnavailableMessage(emptyAnswer)
|
|
490
|
+
: emptyAnswer,
|
|
485
491
|
};
|
|
486
492
|
}
|
|
487
493
|
setResponseStatus(event, upstream.status);
|
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
11
|
-
if (!
|
|
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:
|
|
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.
|
|
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": {
|