@agent-native/core 0.159.3 → 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/chat-first/app-pane.js +1 -1
- package/dist/client/chat-first/copy.js +1 -0
- 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/feature-flags/index.d.ts +0 -2
- package/dist/feature-flags/index.js +3 -2
- package/dist/feature-flags/server.d.ts +2 -0
- package/dist/feature-flags/server.js +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -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 +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat-plugin.js +9 -2
- package/dist/server/auth.js +10 -0
- 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 +2 -2
- package/dist/server/index.js +7 -2
- package/dist/server/onboarding-html.js +16 -257
- 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/shared/auth-copy.d.ts +34 -0
- package/dist/shared/auth-copy.js +293 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/transcription/builder-transcription.js +8 -4
- package/docs/content/actions-advanced.mdx +1 -1
- package/docs/content/portal.mdx +21 -8
- package/package.json +3 -1
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
+
count?: undefined;
|
|
140
141
|
query?: undefined;
|
|
141
142
|
minutes?: undefined;
|
|
142
143
|
limit?: undefined;
|
|
143
144
|
clientHint?: undefined;
|
|
144
145
|
timestamp?: undefined;
|
|
145
146
|
chapterId?: undefined;
|
|
146
|
-
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,6 +159,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
+
count?: undefined;
|
|
162
163
|
query: {
|
|
163
164
|
type: string;
|
|
164
165
|
description: string;
|
|
@@ -174,7 +175,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
174
175
|
clientHint?: undefined;
|
|
175
176
|
timestamp?: undefined;
|
|
176
177
|
chapterId?: undefined;
|
|
177
|
-
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,6 +190,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
+
count?: undefined;
|
|
193
194
|
minutes: {
|
|
194
195
|
type: string;
|
|
195
196
|
description: string;
|
|
@@ -199,7 +200,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
199
200
|
clientHint?: undefined;
|
|
200
201
|
timestamp?: undefined;
|
|
201
202
|
chapterId?: undefined;
|
|
202
|
-
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,6 +216,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
+
count?: undefined;
|
|
219
220
|
query: {
|
|
220
221
|
type: string;
|
|
221
222
|
description: string;
|
|
@@ -234,7 +235,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
234
235
|
};
|
|
235
236
|
timestamp?: undefined;
|
|
236
237
|
chapterId?: undefined;
|
|
237
|
-
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,6 +250,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
+
count?: undefined;
|
|
253
254
|
query?: undefined;
|
|
254
255
|
minutes?: undefined;
|
|
255
256
|
limit?: undefined;
|
|
@@ -263,7 +264,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
263
264
|
description: string;
|
|
264
265
|
};
|
|
265
266
|
chapterId?: undefined;
|
|
266
|
-
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
+
count?: undefined;
|
|
317
318
|
query?: undefined;
|
|
318
319
|
minutes?: undefined;
|
|
319
320
|
limit?: undefined;
|
|
320
321
|
clientHint?: undefined;
|
|
321
322
|
timestamp?: undefined;
|
|
322
323
|
chapterId?: undefined;
|
|
323
|
-
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
+
count?: undefined;
|
|
352
353
|
query?: undefined;
|
|
353
354
|
minutes?: undefined;
|
|
354
355
|
limit?: undefined;
|
|
355
356
|
clientHint?: undefined;
|
|
356
357
|
timestamp?: undefined;
|
|
357
358
|
chapterId?: undefined;
|
|
358
|
-
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|
|
@@ -16,18 +16,18 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
16
16
|
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
count?: undefined;
|
|
19
20
|
updated: number;
|
|
20
21
|
error?: undefined;
|
|
21
22
|
ok?: undefined;
|
|
22
|
-
count?: undefined;
|
|
23
23
|
} | {
|
|
24
|
+
count?: undefined;
|
|
24
25
|
updated?: undefined;
|
|
25
26
|
error: string;
|
|
26
27
|
ok?: undefined;
|
|
27
|
-
count?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
count?: undefined;
|
|
29
30
|
updated?: undefined;
|
|
30
31
|
error?: undefined;
|
|
31
32
|
ok: boolean;
|
|
32
|
-
count?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -41,14 +41,14 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
46
46
|
summary: import("./types.js").TraceSummary;
|
|
47
47
|
spans: import("./types.js").TraceSpan[];
|
|
48
48
|
id?: undefined;
|
|
49
49
|
} | {
|
|
50
|
-
ok?: undefined;
|
|
51
50
|
error?: undefined;
|
|
51
|
+
ok?: undefined;
|
|
52
52
|
summary?: undefined;
|
|
53
53
|
spans?: undefined;
|
|
54
54
|
id: string;
|
|
@@ -75,8 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
75
75
|
user: "user";
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
|
-
message?: undefined;
|
|
79
78
|
id?: undefined;
|
|
79
|
+
message?: undefined;
|
|
80
80
|
deleted?: undefined;
|
|
81
81
|
providers: {
|
|
82
82
|
id: string;
|
|
@@ -93,9 +93,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
93
93
|
registered?: undefined;
|
|
94
94
|
label?: undefined;
|
|
95
95
|
} | {
|
|
96
|
+
id?: undefined;
|
|
96
97
|
message?: undefined;
|
|
97
98
|
count?: undefined;
|
|
98
|
-
id?: undefined;
|
|
99
99
|
deleted?: undefined;
|
|
100
100
|
providers?: undefined;
|
|
101
101
|
found: boolean;
|
|
@@ -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;
|
|
@@ -107,6 +107,31 @@ export function registerFrameworkSecrets() {
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
if (!getRequiredSecret("ANTHROPIC_API_KEY")) {
|
|
111
|
+
registerRequiredSecret({
|
|
112
|
+
key: "ANTHROPIC_API_KEY",
|
|
113
|
+
label: "Anthropic API key",
|
|
114
|
+
description: "Bring your own Claude key instead of routing model calls through Builder.io.",
|
|
115
|
+
docsUrl: "https://console.anthropic.com/settings/keys",
|
|
116
|
+
scope: "user",
|
|
117
|
+
kind: "api-key",
|
|
118
|
+
required: false,
|
|
119
|
+
validator: async (value) => {
|
|
120
|
+
const response = await fetch("https://api.anthropic.com/v1/models", {
|
|
121
|
+
headers: {
|
|
122
|
+
"x-api-key": value,
|
|
123
|
+
"anthropic-version": "2023-06-01",
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
return response.ok
|
|
127
|
+
? { ok: true }
|
|
128
|
+
: {
|
|
129
|
+
ok: false,
|
|
130
|
+
error: `Anthropic rejected the key (HTTP ${response.status}).`,
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
110
135
|
if (!getRequiredSecret("OPENAI_API_KEY")) {
|
|
111
136
|
registerRequiredSecret({
|
|
112
137
|
key: "OPENAI_API_KEY",
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -37,17 +37,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
37
37
|
ok?: undefined;
|
|
38
38
|
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
44
|
ok?: undefined;
|
|
45
45
|
error: string;
|
|
46
46
|
removed?: 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
|
|
@@ -58,13 +58,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
|
|
|
58
58
|
error: string;
|
|
59
59
|
note?: 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
|
}>>;
|
|
@@ -814,11 +814,11 @@ export function createAgentChatPlugin(options) {
|
|
|
814
814
|
try {
|
|
815
815
|
if (webGroupEnabled) {
|
|
816
816
|
const { createWebSearchToolEntry } = await import("../extensions/web-search-tool.js");
|
|
817
|
-
const { getBuilderWebSearchBaseUrl,
|
|
817
|
+
const { getBuilderWebSearchBaseUrl, resolveBuilderGatewayCredentials, resolveSecret, } = await import("./credential-provider.js");
|
|
818
818
|
const { getBuilderGatewayRequestHeaders } = await import("../agent/engine/builder-gateway-headers.js");
|
|
819
819
|
webSearchTool = createWebSearchToolEntry({
|
|
820
820
|
resolveSecret,
|
|
821
|
-
resolveBuilderCredentials,
|
|
821
|
+
resolveBuilderCredentials: resolveBuilderGatewayCredentials,
|
|
822
822
|
getBuilderWebSearchBaseUrl,
|
|
823
823
|
getBuilderRequestHeaders: getBuilderGatewayRequestHeaders,
|
|
824
824
|
});
|
|
@@ -4161,6 +4161,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4161
4161
|
const workerClaim = run.runId
|
|
4162
4162
|
? await readBackgroundRunClaim(run.runId).catch(() => null)
|
|
4163
4163
|
: null;
|
|
4164
|
+
const { isBuilderGatewayDeployConfigured } = await import("./credential-provider.js");
|
|
4164
4165
|
return {
|
|
4165
4166
|
active: true,
|
|
4166
4167
|
runId: run.runId,
|
|
@@ -4177,6 +4178,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4177
4178
|
// `/runs/active?threadId=...` and inspect `diagStage`.
|
|
4178
4179
|
dispatchMode: run.dispatchMode ?? null,
|
|
4179
4180
|
terminalReason: run.terminalReason ?? null,
|
|
4181
|
+
// Who is paying for AI here, which is also who is reading a
|
|
4182
|
+
// failure. `terminalReason` is a bare error CODE, and the client
|
|
4183
|
+
// owns the copy for the handoff failures that never produce an
|
|
4184
|
+
// error event — so without this it has to author credential copy
|
|
4185
|
+
// for a reader it cannot identify, and picks the owner's.
|
|
4186
|
+
deploymentPaysForAi: isBuilderGatewayDeployConfigured(),
|
|
4180
4187
|
diagStage: run.diagStage ?? null,
|
|
4181
4188
|
workerStage: workerClaim?.workerStage ?? null,
|
|
4182
4189
|
// Server clock so the client computes "stuck" elapsed time
|
package/dist/server/auth.js
CHANGED
|
@@ -1981,6 +1981,16 @@ function createAuthGuardFn(app) {
|
|
|
1981
1981
|
p === `${MCP_PUBLIC_ROUTE_PREFIX}/oauth/register`) {
|
|
1982
1982
|
return;
|
|
1983
1983
|
}
|
|
1984
|
+
// Remote hosts authenticate these relay calls with their device token,
|
|
1985
|
+
// not a browser session. Keep this list exact: sibling remote routes such
|
|
1986
|
+
// as register, enqueue, and computer controls are session-authenticated.
|
|
1987
|
+
if (p === "/_agent-native/integrations/remote/unregister" ||
|
|
1988
|
+
p === "/_agent-native/integrations/remote/heartbeat" ||
|
|
1989
|
+
p === "/_agent-native/integrations/remote/poll" ||
|
|
1990
|
+
p === "/_agent-native/integrations/remote/result" ||
|
|
1991
|
+
p === "/_agent-native/integrations/remote/run-events") {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1984
1994
|
// Cross-app SSO ("Sign in with Agent-Native") — CLIENT side. Both the
|
|
1985
1995
|
// `/login` entry point and the `/callback` (hit by a user who is, by
|
|
1986
1996
|
// definition, NOT yet signed in to THIS app) must bypass the blanket
|
|
@@ -83,6 +83,10 @@ export declare function readDeployCredentialEnv(key: string): string | undefined
|
|
|
83
83
|
*/
|
|
84
84
|
export declare function isDeployCredentialFallbackAllowed(): boolean;
|
|
85
85
|
export declare function canUseDeployCredentialFallbackForRequest(key?: string): boolean;
|
|
86
|
+
/** The raw `btk-` gateway PAT a Builder-credits deploy injects. */
|
|
87
|
+
export declare const BUILDER_GATEWAY_TOKEN_ENV_VAR = "BUILDER_GATEWAY_TOKEN";
|
|
88
|
+
/** The space id that pairs with it, sent as `x-builder-api-key`. */
|
|
89
|
+
export declare const BUILDER_GATEWAY_SPACE_ID_ENV_VAR = "BUILDER_GATEWAY_SPACE_ID";
|
|
86
90
|
type BuilderCredentialSource = "user" | "org" | "workspace" | "env";
|
|
87
91
|
export declare function isBuilderPrivateKey(value: string | null | undefined): boolean;
|
|
88
92
|
export interface BuilderCredentialLookupIdentity {
|
|
@@ -177,6 +181,67 @@ export declare function resolveBuilderCredentials(identity?: BuilderCredentialLo
|
|
|
177
181
|
isEnterprise: boolean | null;
|
|
178
182
|
isFreeAccount: boolean | null;
|
|
179
183
|
}>;
|
|
184
|
+
/** `gateway-deploy` means the site pays, so owner-facing copy must not be shown. */
|
|
185
|
+
export type BuilderGatewayLane = "identity" | "gateway-deploy";
|
|
186
|
+
export interface BuilderGatewayCredentialsDetailed extends BuilderCredentialsDetailed {
|
|
187
|
+
lane: BuilderGatewayLane | null;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Both halves required: the gateway 403s a token with no space id before it
|
|
191
|
+
* consults any route policy, so half a pair is not a usable credential.
|
|
192
|
+
*/
|
|
193
|
+
export declare function resolveUsableBuilderGatewayDeployCredentials(): Promise<{
|
|
194
|
+
token: string;
|
|
195
|
+
spaceId: string;
|
|
196
|
+
} | null>;
|
|
197
|
+
/**
|
|
198
|
+
* Weaker than "usable" on purpose: a present-but-rejected token still means the
|
|
199
|
+
* reader is a visitor. Excludes the dev preview, which carries the same token but
|
|
200
|
+
* is read by the project owner.
|
|
201
|
+
*/
|
|
202
|
+
export declare function isBuilderGatewayDeployConfigured(): boolean;
|
|
203
|
+
/** One decision for every gateway-lane consumer; a per-consumer copy drifts. */
|
|
204
|
+
export declare function gatewayLaneUnavailableMessage(ownerFacing: string): string;
|
|
205
|
+
/**
|
|
206
|
+
* Gateway-lane credentials. Fall-through: the request's own Builder connection,
|
|
207
|
+
* then the deployment's credits pair, then the legacy pair.
|
|
208
|
+
*
|
|
209
|
+
* Identity-bearing calls (asset upload, design systems, Admin GraphQL, browser
|
|
210
|
+
* agent) must keep using `resolveBuilderCredentials`: a `['gateway']` token 403s
|
|
211
|
+
* on all of them.
|
|
212
|
+
*/
|
|
213
|
+
export declare function resolveBuilderGatewayCredentialsDetailed(identity?: BuilderCredentialLookupIdentity): Promise<BuilderGatewayCredentialsDetailed>;
|
|
214
|
+
/**
|
|
215
|
+
* Gateway-lane credentials in the same shape as `resolveBuilderCredentials`, so
|
|
216
|
+
* a consumer moves lane by changing which resolver it calls and nothing else.
|
|
217
|
+
*/
|
|
218
|
+
export declare function resolveBuilderGatewayCredentials(identity?: BuilderCredentialLookupIdentity): Promise<{
|
|
219
|
+
privateKey: string | null;
|
|
220
|
+
publicKey: string | null;
|
|
221
|
+
userId: string | null;
|
|
222
|
+
orgName: string | null;
|
|
223
|
+
orgKind: string | null;
|
|
224
|
+
subscription: string | null;
|
|
225
|
+
subscriptionLevel: string | null;
|
|
226
|
+
subscriptionName: string | null;
|
|
227
|
+
isEnterprise: boolean | null;
|
|
228
|
+
isFreeAccount: boolean | null;
|
|
229
|
+
}>;
|
|
230
|
+
export interface BuilderGatewayAuth {
|
|
231
|
+
/** `Bearer <token>` for the `Authorization` header. */
|
|
232
|
+
authorization: string;
|
|
233
|
+
/** Send as `x-builder-api-key`. Null only for a legacy single-key deployment. */
|
|
234
|
+
spaceId: string | null;
|
|
235
|
+
/** Send as `x-builder-user-id` when the lane carries a Builder user. */
|
|
236
|
+
userId: string | null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The gate for gateway-lane features. Not `resolveHasBuilderPrivateKey`, which is
|
|
240
|
+
* identity-only and false on a credits site.
|
|
241
|
+
*/
|
|
242
|
+
export declare function resolveHasBuilderGatewayCredential(): Promise<boolean>;
|
|
243
|
+
/** Gateway-lane `resolveBuilderAuthHeader`, same fall-through order. */
|
|
244
|
+
export declare function resolveBuilderGatewayAuth(): Promise<BuilderGatewayAuth | null>;
|
|
180
245
|
/**
|
|
181
246
|
* Stale markers expire so a rejected model or a transient gateway failure
|
|
182
247
|
* cannot pin a signed-in user to "Builder not connected" forever. Only a
|
|
@@ -235,6 +300,21 @@ export declare function clearProviderCredentialAuthFailure(opts: {
|
|
|
235
300
|
key?: string | null;
|
|
236
301
|
value?: string | null;
|
|
237
302
|
}): Promise<void>;
|
|
303
|
+
/**
|
|
304
|
+
* `builderCredentialFingerprint` needs both legacy keys, so on a credits site the
|
|
305
|
+
* legacy marker is a no-op and a rejected token would be resent forever. The
|
|
306
|
+
* gateway lane fingerprints its single token through the provider marker.
|
|
307
|
+
*/
|
|
308
|
+
export declare function recordBuilderGatewayAuthFailure(details?: {
|
|
309
|
+
status?: number;
|
|
310
|
+
code?: string;
|
|
311
|
+
message?: string;
|
|
312
|
+
}): Promise<void>;
|
|
313
|
+
/** Each clear is a no-op off its own lane, so callers need not know which they are on. */
|
|
314
|
+
export declare function clearBuilderGatewayAuthFailure(creds: {
|
|
315
|
+
privateKey?: string | null;
|
|
316
|
+
publicKey?: string | null;
|
|
317
|
+
}): Promise<void>;
|
|
238
318
|
/**
|
|
239
319
|
* Write Builder credentials to `app_secrets`.
|
|
240
320
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* (e.g. additional Builder-hosted services) without rewrites.
|
|
19
19
|
*/
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
|
-
import { CREDENTIAL_STORE_UNAVAILABLE_ERROR_CODE } from "../agent/engine/credential-errors.js";
|
|
21
|
+
import { CREDENTIAL_STORE_UNAVAILABLE_ERROR_CODE, GATEWAY_UNAVAILABLE_VISITOR_MESSAGE, } from "../agent/engine/credential-errors.js";
|
|
22
22
|
import { getAppConfig } from "../app-config/index.js";
|
|
23
23
|
import { getDbExec, isLocalDatabase, isTransientDatabaseError, } from "../db/client.js";
|
|
24
24
|
import { getOrgSetting } from "../settings/org-settings.js";
|
|
@@ -128,6 +128,12 @@ export function readDeployCredentialEnv(key) {
|
|
|
128
128
|
}
|
|
129
129
|
const APP_PROVIDED_DEPLOY_CREDENTIAL_KEYS = new Set([
|
|
130
130
|
"ANTHROPIC_API_KEY",
|
|
131
|
+
// The Builder-credits pair pays for the deployed app's own model calls and
|
|
132
|
+
// carries no end-user identity — the token is scoped to ['gateway'] and can
|
|
133
|
+
// make no identity-bearing Builder call. The legacy BUILDER_PRIVATE_KEY /
|
|
134
|
+
// BUILDER_PUBLIC_KEY pair can, so it must never be added to this set.
|
|
135
|
+
"BUILDER_GATEWAY_TOKEN",
|
|
136
|
+
"BUILDER_GATEWAY_SPACE_ID",
|
|
131
137
|
"EMAIL_FROM",
|
|
132
138
|
"EMAIL_INBOUND_WEBHOOK_SECRET",
|
|
133
139
|
"EMAIL_AGENT_ADDRESS",
|
|
@@ -180,6 +186,10 @@ export function canUseDeployCredentialFallbackForRequest(key) {
|
|
|
180
186
|
return true;
|
|
181
187
|
return isLocalDatabase();
|
|
182
188
|
}
|
|
189
|
+
/** The raw `btk-` gateway PAT a Builder-credits deploy injects. */
|
|
190
|
+
export const BUILDER_GATEWAY_TOKEN_ENV_VAR = "BUILDER_GATEWAY_TOKEN";
|
|
191
|
+
/** The space id that pairs with it, sent as `x-builder-api-key`. */
|
|
192
|
+
export const BUILDER_GATEWAY_SPACE_ID_ENV_VAR = "BUILDER_GATEWAY_SPACE_ID";
|
|
183
193
|
const BUILDER_CREDENTIAL_KEYS = [
|
|
184
194
|
"BUILDER_PRIVATE_KEY",
|
|
185
195
|
"BUILDER_PUBLIC_KEY",
|
|
@@ -702,6 +712,132 @@ export async function resolveBuilderCredentials(identity) {
|
|
|
702
712
|
isFreeAccount,
|
|
703
713
|
};
|
|
704
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Both halves required: the gateway 403s a token with no space id before it
|
|
717
|
+
* consults any route policy, so half a pair is not a usable credential.
|
|
718
|
+
*/
|
|
719
|
+
export async function resolveUsableBuilderGatewayDeployCredentials() {
|
|
720
|
+
const token = canUseDeployCredentialFallbackForRequest(BUILDER_GATEWAY_TOKEN_ENV_VAR)
|
|
721
|
+
? readDeployCredentialEnv(BUILDER_GATEWAY_TOKEN_ENV_VAR)
|
|
722
|
+
: undefined;
|
|
723
|
+
const spaceId = canUseDeployCredentialFallbackForRequest(BUILDER_GATEWAY_SPACE_ID_ENV_VAR)
|
|
724
|
+
? readDeployCredentialEnv(BUILDER_GATEWAY_SPACE_ID_ENV_VAR)
|
|
725
|
+
: undefined;
|
|
726
|
+
if (!token || !spaceId)
|
|
727
|
+
return null;
|
|
728
|
+
const failure = await getProviderCredentialAuthFailure({
|
|
729
|
+
key: BUILDER_GATEWAY_TOKEN_ENV_VAR,
|
|
730
|
+
value: token,
|
|
731
|
+
});
|
|
732
|
+
return failure ? null : { token, spaceId };
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Weaker than "usable" on purpose: a present-but-rejected token still means the
|
|
736
|
+
* reader is a visitor. Excludes the dev preview, which carries the same token but
|
|
737
|
+
* is read by the project owner.
|
|
738
|
+
*/
|
|
739
|
+
export function isBuilderGatewayDeployConfigured() {
|
|
740
|
+
if (isHostedWorkspaceRuntime())
|
|
741
|
+
return false;
|
|
742
|
+
return Boolean(readDeployCredentialEnv(BUILDER_GATEWAY_TOKEN_ENV_VAR));
|
|
743
|
+
}
|
|
744
|
+
/** One decision for every gateway-lane consumer; a per-consumer copy drifts. */
|
|
745
|
+
export function gatewayLaneUnavailableMessage(ownerFacing) {
|
|
746
|
+
return isBuilderGatewayDeployConfigured()
|
|
747
|
+
? GATEWAY_UNAVAILABLE_VISITOR_MESSAGE
|
|
748
|
+
: ownerFacing;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Gateway-lane credentials. Fall-through: the request's own Builder connection,
|
|
752
|
+
* then the deployment's credits pair, then the legacy pair.
|
|
753
|
+
*
|
|
754
|
+
* Identity-bearing calls (asset upload, design systems, Admin GraphQL, browser
|
|
755
|
+
* agent) must keep using `resolveBuilderCredentials`: a `['gateway']` token 403s
|
|
756
|
+
* on all of them.
|
|
757
|
+
*/
|
|
758
|
+
export async function resolveBuilderGatewayCredentialsDetailed(identity) {
|
|
759
|
+
const scoped = await resolveBuilderCredentialsDetailed(identity);
|
|
760
|
+
if (scoped.source && scoped.source !== "env") {
|
|
761
|
+
return { ...scoped, lane: "identity" };
|
|
762
|
+
}
|
|
763
|
+
// A COMPLETE legacy pair in env is owner-configured, not deploy-injected, so it
|
|
764
|
+
// outranks the gateway here — `selectDetectedEngine` gives it priority for the
|
|
765
|
+
// same reason, and letting the gateway win would pick `builder` on the
|
|
766
|
+
// customer's own pair and then bill the call to the project's gateway space.
|
|
767
|
+
// An incomplete pair is not a usable credential and still falls through.
|
|
768
|
+
if (scoped.privateKey && scoped.publicKey) {
|
|
769
|
+
return { ...scoped, lane: "identity" };
|
|
770
|
+
}
|
|
771
|
+
const gateway = await resolveUsableBuilderGatewayDeployCredentials();
|
|
772
|
+
if (gateway) {
|
|
773
|
+
// Same discipline as `resolveBuilderCredential`: a deploy fallback must not
|
|
774
|
+
// turn an unreadable credential store into a clean connected result.
|
|
775
|
+
assertCredentialStoreReadable(scoped);
|
|
776
|
+
return {
|
|
777
|
+
privateKey: gateway.token,
|
|
778
|
+
publicKey: gateway.spaceId,
|
|
779
|
+
userId: null,
|
|
780
|
+
orgName: null,
|
|
781
|
+
orgKind: null,
|
|
782
|
+
subscription: null,
|
|
783
|
+
subscriptionLevel: null,
|
|
784
|
+
subscriptionName: null,
|
|
785
|
+
isEnterprise: null,
|
|
786
|
+
isFreeAccount: null,
|
|
787
|
+
source: "env",
|
|
788
|
+
lookupFailed: scoped.lookupFailed,
|
|
789
|
+
cause: scoped.cause,
|
|
790
|
+
lane: "gateway-deploy",
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return { ...scoped, lane: scoped.source ? "identity" : null };
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Gateway-lane credentials in the same shape as `resolveBuilderCredentials`, so
|
|
797
|
+
* a consumer moves lane by changing which resolver it calls and nothing else.
|
|
798
|
+
*/
|
|
799
|
+
export async function resolveBuilderGatewayCredentials(identity) {
|
|
800
|
+
const { privateKey, publicKey, userId, orgName, orgKind, subscription, subscriptionLevel, subscriptionName, isEnterprise, isFreeAccount, } = await resolveBuilderGatewayCredentialsDetailed(identity);
|
|
801
|
+
return {
|
|
802
|
+
privateKey,
|
|
803
|
+
publicKey,
|
|
804
|
+
userId,
|
|
805
|
+
orgName,
|
|
806
|
+
orgKind,
|
|
807
|
+
subscription,
|
|
808
|
+
subscriptionLevel,
|
|
809
|
+
subscriptionName,
|
|
810
|
+
isEnterprise,
|
|
811
|
+
isFreeAccount,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* The gate for gateway-lane features. Not `resolveHasBuilderPrivateKey`, which is
|
|
816
|
+
* identity-only and false on a credits site.
|
|
817
|
+
*/
|
|
818
|
+
export async function resolveHasBuilderGatewayCredential() {
|
|
819
|
+
return Boolean(await resolveBuilderGatewayAuth());
|
|
820
|
+
}
|
|
821
|
+
/** Gateway-lane `resolveBuilderAuthHeader`, same fall-through order. */
|
|
822
|
+
export async function resolveBuilderGatewayAuth() {
|
|
823
|
+
const creds = await resolveBuilderGatewayCredentialsDetailed();
|
|
824
|
+
const token = creds.privateKey?.trim();
|
|
825
|
+
const spaceId = creds.publicKey?.trim();
|
|
826
|
+
if (token && spaceId) {
|
|
827
|
+
return {
|
|
828
|
+
authorization: `Bearer ${token}`,
|
|
829
|
+
spaceId,
|
|
830
|
+
userId: creds.userId?.trim() || null,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
// Single-key deployments predate the space id and still authenticate on a
|
|
834
|
+
// `bpk-` private key alone. A gateway token never reaches this branch — its
|
|
835
|
+
// pair is required above.
|
|
836
|
+
const legacyKey = (await resolveBuilderPrivateKey())?.trim();
|
|
837
|
+
return legacyKey
|
|
838
|
+
? { authorization: `Bearer ${legacyKey}`, spaceId: null, userId: null }
|
|
839
|
+
: null;
|
|
840
|
+
}
|
|
705
841
|
const BUILDER_AUTH_FAILURE_SETTING_PREFIX = "builder-auth-failure:";
|
|
706
842
|
/**
|
|
707
843
|
* Stale markers expire so a rejected model or a transient gateway failure
|
|
@@ -882,6 +1018,37 @@ export async function clearProviderCredentialAuthFailure(opts) {
|
|
|
882
1018
|
// A stale failure marker should not block writing or using fresh keys.
|
|
883
1019
|
}
|
|
884
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* `builderCredentialFingerprint` needs both legacy keys, so on a credits site the
|
|
1023
|
+
* legacy marker is a no-op and a rejected token would be resent forever. The
|
|
1024
|
+
* gateway lane fingerprints its single token through the provider marker.
|
|
1025
|
+
*/
|
|
1026
|
+
export async function recordBuilderGatewayAuthFailure(details) {
|
|
1027
|
+
try {
|
|
1028
|
+
const creds = await resolveBuilderGatewayCredentialsDetailed();
|
|
1029
|
+
if (creds.lane === "gateway-deploy" && creds.privateKey) {
|
|
1030
|
+
await recordProviderCredentialAuthFailure({
|
|
1031
|
+
key: BUILDER_GATEWAY_TOKEN_ENV_VAR,
|
|
1032
|
+
value: creds.privateKey,
|
|
1033
|
+
...details,
|
|
1034
|
+
});
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
catch {
|
|
1039
|
+
// Best-effort marker only; the chat error is still returned to the caller.
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
await recordBuilderCredentialAuthFailure(details);
|
|
1043
|
+
}
|
|
1044
|
+
/** Each clear is a no-op off its own lane, so callers need not know which they are on. */
|
|
1045
|
+
export async function clearBuilderGatewayAuthFailure(creds) {
|
|
1046
|
+
await clearBuilderCredentialAuthFailure(creds);
|
|
1047
|
+
await clearProviderCredentialAuthFailure({
|
|
1048
|
+
key: BUILDER_GATEWAY_TOKEN_ENV_VAR,
|
|
1049
|
+
value: creds.privateKey,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
885
1052
|
/**
|
|
886
1053
|
* Write Builder credentials to `app_secrets`.
|
|
887
1054
|
*
|
|
@@ -8,6 +8,6 @@ export declare function resolveGoogleRealtimeCredentials(opts: {
|
|
|
8
8
|
orgId?: string | null;
|
|
9
9
|
}): Promise<string | null>;
|
|
10
10
|
export declare function createGoogleRealtimeSessionHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<GoogleRealtimeSessionResponse | {
|
|
11
|
-
error:
|
|
11
|
+
error: string;
|
|
12
12
|
}>>;
|
|
13
13
|
export {};
|