@agent-native/core 0.209.0-nightly-20261014153330 → 0.209.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/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +68 -3
- package/dist/agent/strict-tool-input.d.ts +4 -0
- package/dist/agent/strict-tool-input.js +26 -0
- package/dist/agent/types.d.ts +7 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/skills-content/turn-into-app-attachments.d.ts +1 -1
- package/dist/cli/skills-content/turn-into-app-attachments.js +1 -3
- package/dist/cli/skills-content/turn-into-app-skill.d.ts +2 -2
- package/dist/cli/skills-content/turn-into-app-skill.js +15 -10
- package/dist/client/chat/agentkit-protocol.js +2 -0
- package/dist/client/chat/runtime.d.ts +1 -0
- package/dist/client/chat/runtime.js +35 -3
- package/dist/collab/routes.d.ts +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/observability/metrics.d.ts +2 -0
- package/dist/observability/metrics.js +15 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/observability/traces.js +8 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +10 -10
- package/dist/server/agent-chat/additional-actions.d.ts +45 -0
- package/dist/server/agent-chat/additional-actions.js +707 -0
- package/dist/server/agent-chat/plugin-options.d.ts +21 -0
- package/dist/server/agent-chat-plugin.js +54 -13
- package/dist/server/better-auth-instance.js +108 -18
- package/dist/server/builder-browser.d.ts +4 -2
- package/dist/server/builder-browser.js +21 -16
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +1 -1
- package/dist/shared/data-url.d.ts +2 -0
- package/dist/shared/data-url.js +12 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/attachments.md +1 -3
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +13 -8
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +2 -2
- package/dist/triggers/actions/manage-automation.d.ts +5 -5
- package/docs/content/agent-surfaces.mdx +61 -0
- package/docs/content/observability.mdx +2 -0
- package/package.json +5 -4
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/attachments.md +1 -3
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +13 -8
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +2 -2
|
@@ -6,6 +6,7 @@ import type { McpActionEntryOptions } from "../../mcp-client/index.js";
|
|
|
6
6
|
import type { ExternalAgentPolicy } from "../../mcp/external-agent-policy.js";
|
|
7
7
|
import type { DatabaseToolsOption } from "../../scripts/db/tool-mode.js";
|
|
8
8
|
import type { PromptExamples } from "../prompts/index.js";
|
|
9
|
+
import type { AgentChatAdditionalActionsExternalSurface, ResolveAgentChatAdditionalActions } from "./additional-actions.js";
|
|
9
10
|
import type { AgentChatMcpIcon, AgentChatMcpOptions } from "./mcp-options.js";
|
|
10
11
|
export type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
|
|
11
12
|
export interface AgentChatPluginOptions {
|
|
@@ -102,6 +103,26 @@ export interface AgentChatPluginOptions {
|
|
|
102
103
|
finalResponseGuard?: AgentLoopFinalResponseGuard;
|
|
103
104
|
prepareRequest?: ProductionAgentOptions["prepareRequest"];
|
|
104
105
|
resolveActionSurface?: ProductionAgentOptions["resolveActionSurface"];
|
|
106
|
+
/**
|
|
107
|
+
* Adds actions for the signed-in caller on each request, such as tools whose
|
|
108
|
+
* names are only known at runtime. Called once per run, before `tool-search`
|
|
109
|
+
* and `resolveActionSurface`, so both see the entries; `surface` says which
|
|
110
|
+
* loop is asking. Interactive chat always calls it; A2A and MCP `ask_app`
|
|
111
|
+
* call it only when listed in `additionalActionSurfaces`.
|
|
112
|
+
*
|
|
113
|
+
* Entries are treated as untrusted. One whose name is malformed, reserved by
|
|
114
|
+
* a framework, template, dev, or MCP action (even one hidden from the
|
|
115
|
+
* agent), or an `mcp__` name this caller cannot see is ignored with a
|
|
116
|
+
* warning. Descriptions are cleaned and capped, and schemas must be bounded
|
|
117
|
+
* object schemas. Every call rechecks that it runs for the caller the entry
|
|
118
|
+
* was resolved for, enforces the entry's `access` policy, and validates its
|
|
119
|
+
* arguments without type coercion. A durable background continuation keeps
|
|
120
|
+
* the turn's original names. Not called for anonymous requests, scheduled
|
|
121
|
+
* jobs, automations, or agent-team sub-agents.
|
|
122
|
+
*/
|
|
123
|
+
resolveAdditionalActions?: ResolveAgentChatAdditionalActions;
|
|
124
|
+
/** Surfaces besides interactive chat that receive `resolveAdditionalActions` entries. */
|
|
125
|
+
additionalActionSurfaces?: readonly AgentChatAdditionalActionsExternalSurface[];
|
|
105
126
|
leanPrompt?: boolean;
|
|
106
127
|
skipFilesContext?: boolean;
|
|
107
128
|
initialToolNames?: string[];
|
|
@@ -173,6 +173,7 @@ import { buildSystemManifestSections, setContextXraySystemSections, } from "../a
|
|
|
173
173
|
// `createAgentChatPlugin` below stays a thinner orchestrator.
|
|
174
174
|
// ---------------------------------------------------------------------------
|
|
175
175
|
import { buildSelectedA2AReceiverContext, createA2AEngineToolSurface, DEFAULT_DELEGATED_MAX_ITERATIONS, DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS, DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS, filterAgentTools, filterMcpOnlyActions, filterPublicAgentActions, filterDirectA2AActions, filterReadOnlyActions, isSelectedA2AReceiver, shouldSelectedA2AReceiverOwnObjective, resolveInitialToolNames, runA2AAgentLoop, runMCPAgentLoop, assembleA2AFinalResponse, buildPublicAgentA2ASkills, buildAuthenticatedAgentA2ASkills, resolveArtifactBaseUrl, unverifiedA2AUserError, } from "./agent-chat/action-filters-a2a.js";
|
|
176
|
+
import { normalizeAdditionalActionSurfaces, resolveAppAdditionalActions, } from "./agent-chat/additional-actions.js";
|
|
176
177
|
import { createBuilderBrowserTool, createTeamTools, } from "./agent-chat/browser-team-tools.js";
|
|
177
178
|
import { createDataWidgetActionEntries, createFrameworkContextEntry, createRefreshScreenEntry, createUrlTools, } from "./agent-chat/context-tools.js";
|
|
178
179
|
import { _agentChatPromptSectionsForTests, buildFrameworkPrompts, buildSchemaBlock, collectFiles, corpusToolNamesTaughtByPrompt, generateActionsPrompt, generateCorpusToolsPrompt, } from "./agent-chat/framework-prompts.js";
|
|
@@ -762,6 +763,28 @@ export function createAgentChatPlugin(options) {
|
|
|
762
763
|
...(toolNames ? { toolNames } : {}),
|
|
763
764
|
});
|
|
764
765
|
};
|
|
766
|
+
// Every registry any surface can dispatch to a built-in implementation,
|
|
767
|
+
// pushed as each one is assembled below. App-supplied request actions
|
|
768
|
+
// may not reuse any of these names, even on a surface (lean chat, A2A,
|
|
769
|
+
// ask_app) that leaves the built-in out, or an allowlist or approval
|
|
770
|
+
// granted to that name could be redirected to app code.
|
|
771
|
+
const reservedActionRegistries = [];
|
|
772
|
+
const additionalActionsConfig = {
|
|
773
|
+
resolver: options?.resolveAdditionalActions,
|
|
774
|
+
externalSurfaces: normalizeAdditionalActionSurfaces(options?.additionalActionSurfaces),
|
|
775
|
+
isReservedName: (name) => reservedActionRegistries.some((registry) => Object.hasOwn(registry, name)),
|
|
776
|
+
};
|
|
777
|
+
// The one place a request's caller-scoped actions are resolved — the
|
|
778
|
+
// caller's MCP tools plus the app's additional actions — for chat, A2A,
|
|
779
|
+
// and ask_app alike. Call it once per run, before tool-search and
|
|
780
|
+
// surface filtering, and reuse the result for the whole run.
|
|
781
|
+
const getRequestScopedActions = async (details, registered) => {
|
|
782
|
+
const mcpEntries = await getMcpActionEntriesForPrincipal(details.ownerEmail, details.orgId);
|
|
783
|
+
return {
|
|
784
|
+
...mcpEntries,
|
|
785
|
+
...(await resolveAppAdditionalActions(additionalActionsConfig, details, { ...registered, ...mcpEntries })),
|
|
786
|
+
};
|
|
787
|
+
};
|
|
765
788
|
const getJobMcpActionEntries = async (job) => {
|
|
766
789
|
const requested = job?.meta.mcpTools ?? [];
|
|
767
790
|
const toolNames = resolveBackgroundMcpToolSelection(requested, options?.backgroundMcpTools === "all");
|
|
@@ -1578,6 +1601,7 @@ export function createAgentChatPlugin(options) {
|
|
|
1578
1601
|
const externalFullActions = mcpFullActions
|
|
1579
1602
|
? { ...mcpOnlyActions, ...mcpFullActions }
|
|
1580
1603
|
: undefined;
|
|
1604
|
+
reservedActionRegistries.push(templateScriptsAll, discoveredActionsAll, devScriptsForA2A, externalActions, ...(externalFullActions ? [externalFullActions] : []));
|
|
1581
1605
|
const { mountA2A } = await import("../a2a/server.js");
|
|
1582
1606
|
const localA2AConfig = {
|
|
1583
1607
|
appId: options?.appId,
|
|
@@ -1909,8 +1933,8 @@ export function createAgentChatPlugin(options) {
|
|
|
1909
1933
|
// repetition guard kills the run minutes later. A rejected `{}` call
|
|
1910
1934
|
// returns a schema error the model can fix on the next step, which is
|
|
1911
1935
|
// strictly better than a shell loop nobody can see.
|
|
1912
|
-
const
|
|
1913
|
-
const
|
|
1936
|
+
const a2aOrgId = getRequestOrgId();
|
|
1937
|
+
const a2aRegisteredActions = devActive
|
|
1914
1938
|
? {
|
|
1915
1939
|
...templateScripts,
|
|
1916
1940
|
...resourceScripts,
|
|
@@ -1930,7 +1954,6 @@ export function createAgentChatPlugin(options) {
|
|
|
1930
1954
|
...coreEmailTools,
|
|
1931
1955
|
...coreAttachmentTools,
|
|
1932
1956
|
...browserTools,
|
|
1933
|
-
...requestMcpActionEntries,
|
|
1934
1957
|
...devScriptsForA2A,
|
|
1935
1958
|
...devRunCodeTool,
|
|
1936
1959
|
}
|
|
@@ -1955,10 +1978,18 @@ export function createAgentChatPlugin(options) {
|
|
|
1955
1978
|
...coreEmailTools,
|
|
1956
1979
|
...coreAttachmentTools,
|
|
1957
1980
|
...browserTools,
|
|
1958
|
-
...requestMcpActionEntries,
|
|
1959
1981
|
...(resolvedProdCodeExec !== "off" ? runCodeTool : {}),
|
|
1960
1982
|
...prodCodingTools,
|
|
1961
|
-
}
|
|
1983
|
+
};
|
|
1984
|
+
const a2aActions = attachToolSearch({
|
|
1985
|
+
...a2aRegisteredActions,
|
|
1986
|
+
...(await getRequestScopedActions({
|
|
1987
|
+
surface: "a2a",
|
|
1988
|
+
event: context.event,
|
|
1989
|
+
ownerEmail: userEmail,
|
|
1990
|
+
orgId: a2aOrgId,
|
|
1991
|
+
}, a2aRegisteredActions)),
|
|
1992
|
+
});
|
|
1962
1993
|
const a2aToolSurface = createA2AEngineToolSurface(actionsToEngineTools(a2aActions), effectiveInitialToolNames, {
|
|
1963
1994
|
receiverOwnsObjective,
|
|
1964
1995
|
// Same curated set the MCP mount serves: config names plus the
|
|
@@ -2031,6 +2062,9 @@ export function createAgentChatPlugin(options) {
|
|
|
2031
2062
|
// scope when a processor hop or alternate runner is involved.
|
|
2032
2063
|
ownerEmail: userEmail,
|
|
2033
2064
|
orgId: getRequestOrgId() ?? null,
|
|
2065
|
+
// Without it, every action with an `access` policy is refused
|
|
2066
|
+
// for lack of an application identity.
|
|
2067
|
+
appId: options?.appId,
|
|
2034
2068
|
executionMode: "act",
|
|
2035
2069
|
runId: context.taskId,
|
|
2036
2070
|
networkProtocol: "a2a",
|
|
@@ -2406,8 +2440,8 @@ export function createAgentChatPlugin(options) {
|
|
|
2406
2440
|
// here for the same reason as the A2A surface above: an external
|
|
2407
2441
|
// caller cannot nurse a shell command that misfires.
|
|
2408
2442
|
const devActiveMcp = isDevMode();
|
|
2409
|
-
const
|
|
2410
|
-
const
|
|
2443
|
+
const mcpOrgId = getRequestOrgId();
|
|
2444
|
+
const mcpRegisteredActions = devActiveMcp
|
|
2411
2445
|
? {
|
|
2412
2446
|
...templateScripts,
|
|
2413
2447
|
...resourceScripts,
|
|
@@ -2420,7 +2454,6 @@ export function createAgentChatPlugin(options) {
|
|
|
2420
2454
|
...workspaceFilesTool,
|
|
2421
2455
|
...workspaceFileActions,
|
|
2422
2456
|
...toolActions,
|
|
2423
|
-
...requestMcpActionEntries,
|
|
2424
2457
|
...devScriptsForA2A,
|
|
2425
2458
|
...devRunCodeTool,
|
|
2426
2459
|
}
|
|
@@ -2438,10 +2471,13 @@ export function createAgentChatPlugin(options) {
|
|
|
2438
2471
|
...workspaceFilesTool,
|
|
2439
2472
|
...workspaceFileActions,
|
|
2440
2473
|
...toolActions,
|
|
2441
|
-
...requestMcpActionEntries,
|
|
2442
2474
|
...(resolvedProdCodeExec !== "off" ? runCodeTool : {}),
|
|
2443
2475
|
...prodCodingTools,
|
|
2444
|
-
}
|
|
2476
|
+
};
|
|
2477
|
+
const mcpActions = attachToolSearch({
|
|
2478
|
+
...mcpRegisteredActions,
|
|
2479
|
+
...(await getRequestScopedActions({ surface: "mcp", ownerEmail, orgId: mcpOrgId }, mcpRegisteredActions)),
|
|
2480
|
+
});
|
|
2445
2481
|
// Same compact initial-tool surface as interactive chat and A2A:
|
|
2446
2482
|
// template actions + the small framework default set stay visible
|
|
2447
2483
|
// on the first request; everything else (provider, MCP, extension
|
|
@@ -2492,6 +2528,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2492
2528
|
actions: mcpActions,
|
|
2493
2529
|
ownerEmail,
|
|
2494
2530
|
orgId: getRequestOrgId() ?? null,
|
|
2531
|
+
appId: options?.appId,
|
|
2495
2532
|
executionMode: "act",
|
|
2496
2533
|
runId: mcpRunId,
|
|
2497
2534
|
networkProtocol: "mcp",
|
|
@@ -2585,6 +2622,7 @@ export function createAgentChatPlugin(options) {
|
|
|
2585
2622
|
catch {
|
|
2586
2623
|
// Ignore — templates without sharing still work.
|
|
2587
2624
|
}
|
|
2625
|
+
reservedActionRegistries.push(httpActions);
|
|
2588
2626
|
const { mountActionRoutes, mountWebMcpActionRoutes } = await import("./action-routes.js");
|
|
2589
2627
|
const directoryProfile = mcpOptions.enabled
|
|
2590
2628
|
? mcpOptions.directoryProfile
|
|
@@ -3320,6 +3358,7 @@ export function createAgentChatPlugin(options) {
|
|
|
3320
3358
|
: {}),
|
|
3321
3359
|
});
|
|
3322
3360
|
leanRunCodeToolActions = leanActions;
|
|
3361
|
+
reservedActionRegistries.push(prodActions, leanActions);
|
|
3323
3362
|
// Always build the production handler (includes resource tools + call-agent + team tools)
|
|
3324
3363
|
// In production mode (!canToggle), resolve the owner from the request session.
|
|
3325
3364
|
const isHostedProd = !canToggle;
|
|
@@ -3529,9 +3568,10 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
3529
3568
|
? "\n\n<code-execution-mode>Full shell access is enabled (trusted mode). You have bash, read, edit, write, and run-code tools available. Use bash for file discovery, running tests and builds, and project CLIs. Use run-code for sandboxed JavaScript data processing: provider/API pagination, joins, classification, aggregation, and large-response reduction. Use tool-orchestration for short bounded fan-out or reduction over read-only tools. Use `pnpm action <name>` in bash to invoke registered app actions from the shell.</code-execution-mode>"
|
|
3530
3569
|
: "\n\n<code-execution-mode>Sandboxed code execution is enabled. Use tool-orchestration for short bounded fan-out, joins, and reduction over read-only tools. Use run-code when you need its broader provider/web helpers, workspace staging, or durable background execution. In either tool, authenticated calls go through the provided host globals and results should be reduced before printing.</code-execution-mode>"
|
|
3531
3570
|
: "";
|
|
3571
|
+
const prodHandlerActions = leanPrompt ? leanActions : prodActions;
|
|
3532
3572
|
const prodHandler = createProductionAgentHandler({
|
|
3533
3573
|
assertAiSetupReady: requireAgentChatAiSetup,
|
|
3534
|
-
actions:
|
|
3574
|
+
actions: prodHandlerActions,
|
|
3535
3575
|
systemPrompt: async (event) => {
|
|
3536
3576
|
const { owner, extra } = await prepareRun(event);
|
|
3537
3577
|
const requestActionsPrompt = resolveRequestActionsPrompt("tool");
|
|
@@ -3682,7 +3722,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
3682
3722
|
_runSendByThread.delete(threadId);
|
|
3683
3723
|
await onRunComplete(run, threadId, trackingSource);
|
|
3684
3724
|
},
|
|
3685
|
-
resolveAdditionalActions: ({
|
|
3725
|
+
resolveAdditionalActions: (details) => getRequestScopedActions({ surface: "chat", ...details }, prodHandlerActions),
|
|
3686
3726
|
// Resolve owner from session for usage attribution in hosted prod
|
|
3687
3727
|
resolveOwnerEmail: isHostedProd ? getOwnerFromEvent : undefined,
|
|
3688
3728
|
});
|
|
@@ -3829,10 +3869,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
3829
3869
|
// Wire the late-binding supplier for devRunCodeTool so the bridge can
|
|
3830
3870
|
// call back into the fully-assembled devActions registry.
|
|
3831
3871
|
devRunCodeToolActions = devActions;
|
|
3872
|
+
reservedActionRegistries.push(devActions);
|
|
3832
3873
|
devHandler = createProductionAgentHandler({
|
|
3833
3874
|
assertAiSetupReady: requireAgentChatAiSetup,
|
|
3834
3875
|
actions: devActions,
|
|
3835
|
-
resolveAdditionalActions: ({
|
|
3876
|
+
resolveAdditionalActions: (details) => getRequestScopedActions({ surface: "chat", ...details }, devActions),
|
|
3836
3877
|
systemPrompt: async (event) => {
|
|
3837
3878
|
const { owner, extra } = await prepareRun(event);
|
|
3838
3879
|
const requestActionsPrompt = resolveRequestActionsPrompt(devNative ? "tool" : "cli");
|
|
@@ -8,7 +8,9 @@ function stringifyValue(value) {
|
|
|
8
8
|
import crypto from "node:crypto";
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
|
+
import { getCurrentAuthEndpointContext } from "@better-auth/core/context";
|
|
11
12
|
import { betterAuth, } from "better-auth";
|
|
13
|
+
import { createAuthMiddleware, createEmailVerificationToken, } from "better-auth/api";
|
|
12
14
|
import { bearer } from "better-auth/plugins/bearer";
|
|
13
15
|
import { jwt } from "better-auth/plugins/jwt";
|
|
14
16
|
import { magicLink } from "better-auth/plugins/magic-link";
|
|
@@ -19,6 +21,7 @@ import { enterpriseAuthAdaptersBuilt, getAppConfig, } from "../app-config/index.
|
|
|
19
21
|
import { TEMPLATES } from "../cli/templates-meta.js";
|
|
20
22
|
import { getDbExec } from "../db/client.js";
|
|
21
23
|
import { assertHostedRuntimeDatabase, getRuntimeDatabaseUrl, getPgliteClient, isPgliteUrl, loadPgliteDrizzle, pgliteDrizzleClient, pgPoolOptions, neonPoolOptions, guardNeonPool, sharedDbPool, resolveSharedDbPool, onSharedDbPoolsClosed, onSharedDbPoolReplaced, onDbClientsClosing, } from "../db/client.js";
|
|
24
|
+
import { ensureTable as ensureEmailLogTable } from "../email-catalog/log.js";
|
|
22
25
|
import { CORE_CHANGE_EMAIL_CONFIRMATION_EMAIL_ID, CORE_CHANGE_EMAIL_VERIFICATION_EMAIL_ID, CORE_MAGIC_LINK_EMAIL_ID, CORE_RESET_PASSWORD_EMAIL_ID, CORE_VERIFY_SIGNUP_EMAIL_ID, } from "../email-catalog/system-emails.js";
|
|
23
26
|
import { renderTransactionalEmail } from "../email-catalog/templates.js";
|
|
24
27
|
import { executeIdentityRekey, rekeyIdentity, resumePendingIdentityRekeys, verifiedEmailChangeFromToken, } from "../identity/rekey.js";
|
|
@@ -80,6 +83,9 @@ async function preflightEmailIdentityRekey(oldEmail, newEmail) {
|
|
|
80
83
|
});
|
|
81
84
|
}
|
|
82
85
|
export { getAuthLoginMode, resolveAuthLoginMode, resolveAuthLoginModeFromReadiness, } from "./auth-login-mode.js";
|
|
86
|
+
const EXISTING_ACCOUNT_VERIFICATION_COOLDOWN_MS = 5 * 60_000;
|
|
87
|
+
// Signup delivery holds its transaction, connection, and per-address lock.
|
|
88
|
+
const VERIFICATION_EMAIL_TIMEOUT_MS = 10_000;
|
|
83
89
|
async function flushSignupTracking() {
|
|
84
90
|
try {
|
|
85
91
|
await Promise.race([
|
|
@@ -1559,6 +1565,25 @@ async function createBetterAuthInstance(config) {
|
|
|
1559
1565
|
});
|
|
1560
1566
|
},
|
|
1561
1567
|
});
|
|
1568
|
+
const sendVerificationEmail = async ({ user, url, token }) => {
|
|
1569
|
+
const deliveredVerifyUrl = emailAuthLinkLandingUrl(url) ?? url;
|
|
1570
|
+
const emailChange = await verifiedEmailChangeFromToken(token, secret, user.email);
|
|
1571
|
+
if (emailChange)
|
|
1572
|
+
await preflightEmailIdentityRekey(emailChange.oldEmail, emailChange.newEmail);
|
|
1573
|
+
const renderedEmail = await renderTransactionalEmail(emailChange
|
|
1574
|
+
? CORE_CHANGE_EMAIL_VERIFICATION_EMAIL_ID
|
|
1575
|
+
: CORE_VERIFY_SIGNUP_EMAIL_ID, { email: user.email, verifyUrl: deliveredVerifyUrl });
|
|
1576
|
+
await sendEmail({
|
|
1577
|
+
to: user.email,
|
|
1578
|
+
...renderedEmail,
|
|
1579
|
+
disableClickTracking: true,
|
|
1580
|
+
templateId: emailChange
|
|
1581
|
+
? CORE_CHANGE_EMAIL_VERIFICATION_EMAIL_ID
|
|
1582
|
+
: CORE_VERIFY_SIGNUP_EMAIL_ID,
|
|
1583
|
+
authCritical: true,
|
|
1584
|
+
timeoutMs: VERIFICATION_EMAIL_TIMEOUT_MS,
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1562
1587
|
const auth = betterAuth({
|
|
1563
1588
|
basePath,
|
|
1564
1589
|
baseURL: appUrl,
|
|
@@ -1575,6 +1600,39 @@ async function createBetterAuthInstance(config) {
|
|
|
1575
1600
|
// Email verification is enabled only when a provider is ready. Without
|
|
1576
1601
|
// one, hosted deployments keep password signup available.
|
|
1577
1602
|
requireEmailVerification,
|
|
1603
|
+
onExistingUserSignUp: requireEmailVerification
|
|
1604
|
+
? async ({ user }) => {
|
|
1605
|
+
// Duplicate signup is unauthenticated, and direct auth.api calls
|
|
1606
|
+
// such as the register route skip Better Auth's IP rate limit.
|
|
1607
|
+
// The lock makes a concurrent signup for this address wait for
|
|
1608
|
+
// this transaction, so it sees the sent row before checking.
|
|
1609
|
+
const db = getDbExec();
|
|
1610
|
+
await db.execute({
|
|
1611
|
+
sql: "SELECT pg_advisory_xact_lock(hashtextextended(?, 0::bigint))",
|
|
1612
|
+
args: [`existing-account-verification:${user.email}`],
|
|
1613
|
+
});
|
|
1614
|
+
const { rows } = await db.execute({
|
|
1615
|
+
sql: `SELECT 1 FROM email_log
|
|
1616
|
+
WHERE template_id = ? AND recipient = ? AND status = 'sent' AND created_at > ?
|
|
1617
|
+
LIMIT 1`,
|
|
1618
|
+
args: [
|
|
1619
|
+
CORE_VERIFY_SIGNUP_EMAIL_ID,
|
|
1620
|
+
user.email,
|
|
1621
|
+
Date.now() - EXISTING_ACCOUNT_VERIFICATION_COOLDOWN_MS,
|
|
1622
|
+
],
|
|
1623
|
+
});
|
|
1624
|
+
if (rows.length > 0)
|
|
1625
|
+
return;
|
|
1626
|
+
const endpoint = getCurrentAuthEndpointContext();
|
|
1627
|
+
const callbackURL = typeof endpoint.body?.callbackURL === "string" &&
|
|
1628
|
+
endpoint.body.callbackURL
|
|
1629
|
+
? endpoint.body.callbackURL
|
|
1630
|
+
: "/";
|
|
1631
|
+
const token = await createEmailVerificationToken(secret, user.email);
|
|
1632
|
+
const url = `${endpoint.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(callbackURL)}`;
|
|
1633
|
+
await sendVerificationEmail({ user, url, token });
|
|
1634
|
+
}
|
|
1635
|
+
: undefined,
|
|
1578
1636
|
sendResetPassword: async ({ user, token }) => {
|
|
1579
1637
|
const appBasePath = (process.env.VITE_APP_BASE_PATH ||
|
|
1580
1638
|
process.env.APP_BASE_PATH ||
|
|
@@ -1599,24 +1657,7 @@ async function createBetterAuthInstance(config) {
|
|
|
1599
1657
|
emailVerification: {
|
|
1600
1658
|
sendOnSignUp: requireEmailVerification,
|
|
1601
1659
|
autoSignInAfterVerification: true,
|
|
1602
|
-
sendVerificationEmail
|
|
1603
|
-
const deliveredVerifyUrl = emailAuthLinkLandingUrl(url) ?? url;
|
|
1604
|
-
const emailChange = await verifiedEmailChangeFromToken(token, secret, user.email);
|
|
1605
|
-
if (emailChange)
|
|
1606
|
-
await preflightEmailIdentityRekey(emailChange.oldEmail, emailChange.newEmail);
|
|
1607
|
-
const renderedEmail = await renderTransactionalEmail(emailChange
|
|
1608
|
-
? CORE_CHANGE_EMAIL_VERIFICATION_EMAIL_ID
|
|
1609
|
-
: CORE_VERIFY_SIGNUP_EMAIL_ID, { email: user.email, verifyUrl: deliveredVerifyUrl });
|
|
1610
|
-
await sendEmail({
|
|
1611
|
-
to: user.email,
|
|
1612
|
-
...renderedEmail,
|
|
1613
|
-
disableClickTracking: true,
|
|
1614
|
-
templateId: emailChange
|
|
1615
|
-
? CORE_CHANGE_EMAIL_VERIFICATION_EMAIL_ID
|
|
1616
|
-
: CORE_VERIFY_SIGNUP_EMAIL_ID,
|
|
1617
|
-
authCritical: true,
|
|
1618
|
-
});
|
|
1619
|
-
},
|
|
1660
|
+
sendVerificationEmail,
|
|
1620
1661
|
afterEmailVerification: async (user, request) => {
|
|
1621
1662
|
if (!request)
|
|
1622
1663
|
return;
|
|
@@ -1870,6 +1911,55 @@ async function createBetterAuthInstance(config) {
|
|
|
1870
1911
|
]),
|
|
1871
1912
|
...enterprisePlugins,
|
|
1872
1913
|
...configuredPlugins,
|
|
1914
|
+
{
|
|
1915
|
+
id: "verification-email-delivery",
|
|
1916
|
+
hooks: {
|
|
1917
|
+
before: [
|
|
1918
|
+
{
|
|
1919
|
+
matcher: (ctx) => ctx.path === "/sign-up/email" && requireEmailVerification,
|
|
1920
|
+
handler: createAuthMiddleware(async () => {
|
|
1921
|
+
// A failed delivery rolls signup back; lazy log DDL must not
|
|
1922
|
+
// leave its readiness cache pointing at a rolled-back table.
|
|
1923
|
+
await ensureEmailLogTable();
|
|
1924
|
+
}),
|
|
1925
|
+
},
|
|
1926
|
+
],
|
|
1927
|
+
},
|
|
1928
|
+
init(ctx) {
|
|
1929
|
+
const emailVerification = ctx.options.emailVerification;
|
|
1930
|
+
if (!emailVerification?.sendVerificationEmail)
|
|
1931
|
+
return;
|
|
1932
|
+
const sendVerificationEmail = emailVerification.sendVerificationEmail;
|
|
1933
|
+
const deliveries = new WeakSet();
|
|
1934
|
+
emailVerification.sendVerificationEmail = (...args) => {
|
|
1935
|
+
const delivery = sendVerificationEmail(...args);
|
|
1936
|
+
if (delivery instanceof Promise)
|
|
1937
|
+
deliveries.add(delivery);
|
|
1938
|
+
return delivery;
|
|
1939
|
+
};
|
|
1940
|
+
const passwordOptions = ctx.options.emailAndPassword;
|
|
1941
|
+
const onExistingUserSignUp = passwordOptions?.onExistingUserSignUp;
|
|
1942
|
+
if (passwordOptions && onExistingUserSignUp) {
|
|
1943
|
+
passwordOptions.onExistingUserSignUp = (...args) => {
|
|
1944
|
+
const delivery = onExistingUserSignUp(...args);
|
|
1945
|
+
deliveries.add(delivery);
|
|
1946
|
+
return delivery;
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
const runInBackgroundOrAwait = ctx.runInBackgroundOrAwait.bind(ctx);
|
|
1950
|
+
return {
|
|
1951
|
+
context: {
|
|
1952
|
+
runInBackgroundOrAwait(task) {
|
|
1953
|
+
// Better Auth swallows even awaited task errors; verification
|
|
1954
|
+
// must fail the request before the UI can claim mail was sent.
|
|
1955
|
+
if (task instanceof Promise && deliveries.has(task))
|
|
1956
|
+
return task;
|
|
1957
|
+
return runInBackgroundOrAwait(task);
|
|
1958
|
+
},
|
|
1959
|
+
},
|
|
1960
|
+
};
|
|
1961
|
+
},
|
|
1962
|
+
},
|
|
1873
1963
|
],
|
|
1874
1964
|
});
|
|
1875
1965
|
return auth;
|
|
@@ -261,7 +261,6 @@ export interface BuilderAgentUploadAttachment {
|
|
|
261
261
|
name: string;
|
|
262
262
|
dataUrl: string;
|
|
263
263
|
text?: string;
|
|
264
|
-
size: number;
|
|
265
264
|
id: string;
|
|
266
265
|
}
|
|
267
266
|
export interface BuilderAgentUrlAttachment {
|
|
@@ -269,6 +268,9 @@ export interface BuilderAgentUrlAttachment {
|
|
|
269
268
|
value: string;
|
|
270
269
|
}
|
|
271
270
|
export type BuilderAgentAttachment = BuilderAgentUploadAttachment | BuilderAgentUrlAttachment;
|
|
271
|
+
export type BuilderAgentWireAttachment = (BuilderAgentUploadAttachment & {
|
|
272
|
+
size: number;
|
|
273
|
+
}) | BuilderAgentUrlAttachment;
|
|
272
274
|
export interface RunBuilderAgentArgs {
|
|
273
275
|
prompt: string;
|
|
274
276
|
context?: string;
|
|
@@ -279,7 +281,7 @@ export interface RunBuilderAgentArgs {
|
|
|
279
281
|
userId?: string;
|
|
280
282
|
}
|
|
281
283
|
export declare const BUILDER_AGENT_CONTEXT_MAX_CHARS = 32000;
|
|
282
|
-
export declare function normalizeBuilderAgentAttachments(attachments: BuilderAgentAttachment[] | undefined):
|
|
284
|
+
export declare function normalizeBuilderAgentAttachments(attachments: BuilderAgentAttachment[] | undefined): BuilderAgentWireAttachment[] | undefined;
|
|
283
285
|
export declare function normalizeBuilderAgentContext(value: unknown): string | undefined;
|
|
284
286
|
export declare function buildBuilderAgentUserPrompt(prompt: string, context?: string): string;
|
|
285
287
|
export interface RunBuilderAgentResult {
|
|
@@ -3,7 +3,7 @@ import { getHeader, getQuery, getRequestIP, setResponseHeader, setResponseStatus
|
|
|
3
3
|
import { ActionContractError } from "../action.js";
|
|
4
4
|
import { getSetting } from "../settings/store.js";
|
|
5
5
|
import { applyBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
6
|
-
import { parseBase64DataUrl } from "../shared/data-url.js";
|
|
6
|
+
import { base64ByteLength, isBase64, parseBase64DataUrl, } from "../shared/data-url.js";
|
|
7
7
|
import { getAuthSecret, resolveSignupTrackingIdentity, } from "./better-auth-instance.js";
|
|
8
8
|
import { resolveBuilderRequestAuthorization, } from "./builder-api-auth.js";
|
|
9
9
|
import { firstPublicBuilderPreviewOriginFromEnv, isLoopbackBuilderRequestHost, isTrustedBuilderRequestHost, } from "./builder-preview-origin.js";
|
|
@@ -1561,50 +1561,55 @@ const BUILDER_AGENT_UPLOAD_CONTENT_TYPES = new Set([
|
|
|
1561
1561
|
"application/json",
|
|
1562
1562
|
"text/plain",
|
|
1563
1563
|
]);
|
|
1564
|
+
function invalidAttachment(message) {
|
|
1565
|
+
return new ActionContractError(message, {
|
|
1566
|
+
errorCode: "invalid_attachment",
|
|
1567
|
+
statusCode: 400,
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1564
1570
|
export function normalizeBuilderAgentAttachments(attachments) {
|
|
1565
1571
|
if (!attachments || attachments.length === 0)
|
|
1566
1572
|
return undefined;
|
|
1567
|
-
|
|
1573
|
+
return attachments.map((attachment) => {
|
|
1568
1574
|
if (attachment.type === "url") {
|
|
1569
1575
|
let url;
|
|
1570
1576
|
try {
|
|
1571
1577
|
url = new URL(attachment.value);
|
|
1572
1578
|
}
|
|
1573
1579
|
catch {
|
|
1574
|
-
throw
|
|
1580
|
+
throw invalidAttachment("Builder attachment URL is malformed");
|
|
1575
1581
|
}
|
|
1576
1582
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
1577
|
-
throw
|
|
1583
|
+
throw invalidAttachment("Builder attachment URL must use http or https");
|
|
1578
1584
|
}
|
|
1579
1585
|
return attachment;
|
|
1580
1586
|
}
|
|
1581
1587
|
if (!BUILDER_AGENT_UPLOAD_CONTENT_TYPES.has(attachment.contentType)) {
|
|
1582
|
-
throw
|
|
1588
|
+
throw invalidAttachment(`Unsupported Builder attachment content type: ${attachment.contentType}`);
|
|
1583
1589
|
}
|
|
1584
1590
|
if (!attachment.name.trim() || !attachment.id.trim()) {
|
|
1585
|
-
throw
|
|
1586
|
-
}
|
|
1587
|
-
if (!Number.isInteger(attachment.size) || attachment.size < 0) {
|
|
1588
|
-
throw new Error("Builder attachment size must be a non-negative integer");
|
|
1591
|
+
throw invalidAttachment("Builder upload attachments require a name and id");
|
|
1589
1592
|
}
|
|
1593
|
+
let size;
|
|
1590
1594
|
if (attachment.contentType === "text/plain" ||
|
|
1591
1595
|
attachment.contentType === "application/json") {
|
|
1592
1596
|
if (attachment.text === undefined || attachment.dataUrl !== "") {
|
|
1593
|
-
throw
|
|
1594
|
-
}
|
|
1595
|
-
if (Buffer.byteLength(attachment.text, "utf8") !== attachment.size) {
|
|
1596
|
-
throw new Error("Builder attachment size does not match its text content");
|
|
1597
|
+
throw invalidAttachment("Text and JSON Builder attachments require text and an empty dataUrl");
|
|
1597
1598
|
}
|
|
1599
|
+
size = Buffer.byteLength(attachment.text, "utf8");
|
|
1598
1600
|
}
|
|
1599
1601
|
else {
|
|
1600
1602
|
const dataUrl = parseBase64DataUrl(attachment.dataUrl);
|
|
1601
1603
|
if (!dataUrl || dataUrl.mediaType !== attachment.contentType) {
|
|
1602
|
-
throw
|
|
1604
|
+
throw invalidAttachment("Image and PDF Builder attachments require a matching base64 dataUrl");
|
|
1605
|
+
}
|
|
1606
|
+
if (!isBase64(dataUrl.data)) {
|
|
1607
|
+
throw invalidAttachment("Image and PDF Builder attachments require valid base64 data");
|
|
1603
1608
|
}
|
|
1609
|
+
size = base64ByteLength(dataUrl.data);
|
|
1604
1610
|
}
|
|
1605
|
-
return attachment;
|
|
1611
|
+
return { ...attachment, size };
|
|
1606
1612
|
});
|
|
1607
|
-
return normalized;
|
|
1608
1613
|
}
|
|
1609
1614
|
export function normalizeBuilderAgentContext(value) {
|
|
1610
1615
|
if (value == null)
|
package/dist/server/index.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export { createObservationalMemoryPlugin, defaultObservationalMemoryPlugin, } fr
|
|
|
50
50
|
export { createGoogleAuthPlugin, type GoogleAuthPluginOptions, } from "./google-auth-plugin.js";
|
|
51
51
|
export type { GoogleAuthMode } from "./google-auth-mode.js";
|
|
52
52
|
export { createAgentChatPlugin, defaultAgentChatPlugin, loadResourcesForPrompt, type AgentChatPluginOptions, } from "./agent-chat-plugin.js";
|
|
53
|
+
export type { AgentChatAdditionalActionsDetails, AgentChatAdditionalActionsExternalSurface, AgentChatAdditionalActionsSurface, ResolveAgentChatAdditionalActions, } from "./agent-chat/additional-actions.js";
|
|
53
54
|
export { buildFrameworkPrompts, generateActionsPrompt, } from "./agent-chat/framework-prompts.js";
|
|
54
55
|
export { refreshMcpManagerForPrincipal } from "./agent-chat/mcp-glue.js";
|
|
55
56
|
export { AGENT_CHAT_STREAM_PATH, AGENT_CHAT_STREAM_TOKEN_SUFFIX, AGENT_CHAT_STREAM_TOKEN_TTL_SECONDS, createAgentChatStreamToken, isAgentChatStreamingRuntime, readAgentChatStreamBearerToken, verifyAgentChatStreamToken, type AgentChatStreamPrincipal, } from "./agent-chat-stream.js";
|
|
@@ -99,7 +100,7 @@ export { isServerlessRuntime } from "../db/client.js";
|
|
|
99
100
|
export { BUILDER_PUBLISH_MCP_RESOURCE, canAuthorizeBuilderApiRequest, hasBuilderApiCredentialCustody, resolveBuilderApiAuthorization, resolveBuilderLegacyRequestAuthorization, resolveBuilderRequestAuthorization, type BuilderLegacyCredentialKey, type BuilderRequestAuthorization, } from "./builder-api-auth.js";
|
|
100
101
|
export { BUILDER_ASSETS_WRITE_SCOPE, BUILDER_OAUTH_SCOPE, type BuilderOAuthPermissionScope, } from "./builder-oauth.js";
|
|
101
102
|
export { assertBuilderDesignSystemCodeIndexingAllowed, builderDesignSystemUrl, builderProjectBranchUrl, buildBuilderDesignSystemIndexFiles, collectBuilderDesignSystemGitHubFiles, createBuilderDesignSystemProxyFields, designSystemTierUpgradeUrl, fetchBuilderDesignSystemDecodeJobStatus, fetchBuilderDesignSystemDocs, fetchBuilderDesignSystemDocumentCount, fetchBuilderDesignSystemRecord, fetchBuilderDesignSystemTierLimit, getBuilderDesignSystemsBaseUrl, hydrateBuilderDesignSystemReference, indexBuilderDesignSystem, isBuilderDesignSystemReadyByCount, localBuilderDesignSystemId, mimeTypeForBuilderDesignSystemFilename, parseBuilderDesignSystemProxyReference, startBuilderDesignSystemIndex, startBuilderDesignSystemUpload, type BuildBuilderDesignSystemIndexFilesOptions, type BuilderDesignSystemCodeFileInput, type BuilderDesignSystemDecodeJobStatus, type BuilderDesignSystemDocsOptions, type BuilderDesignSystemDocument, type BuilderDesignSystemDocumentCountResult, type BuilderDesignSystemHydratedReference, type BuilderDesignSystemIndexFile, type BuilderDesignSystemIndexFromSourcesOptions, type BuilderDesignSystemIndexOptions, type BuilderDesignSystemIndexResult, type BuilderDesignSystemRecord, type BuilderDesignSystemStatus, type BuilderDesignSystemTierLimit, type BuilderDesignSystemGitHubFile, type BuilderDesignSystemGitHubFileCollection, type BuilderDesignSystemGitHubSource, type BuilderDesignSystemUploadAttachment, type BuilderDesignSystemUploadSlot, type BuilderDesignSystemProxyFields, type BuilderDesignSystemProxyFieldsOptions, type BuilderDesignSystemProxyReference, type BuilderDesignSystemSourceKind, } from "./builder-design-systems.js";
|
|
102
|
-
export { cdnSafeOriginStatus, createBuilderProject, ensureBuilderProject, findBuilderProjectForRepo, getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, runBuilderAgent, type BuilderAgentAttachment, type BuilderAgentUploadAttachment, type BuilderAgentUrlAttachment, type BuilderProjectResult, type RunBuilderAgentResult, } from "./builder-browser.js";
|
|
103
|
+
export { cdnSafeOriginStatus, createBuilderProject, ensureBuilderProject, findBuilderProjectForRepo, getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, normalizeBuilderAgentAttachments, runBuilderAgent, type BuilderAgentAttachment, type BuilderAgentUploadAttachment, type BuilderAgentUrlAttachment, type BuilderProjectResult, type RunBuilderAgentResult, } from "./builder-browser.js";
|
|
103
104
|
export { ensureFusionContainer, sendFusionBranchMessage, pushFusionBranch, reserveFusionHostingSlug, deployFusionProject, getFusionDeploys, getFusionBranchEditorUrl, getFusionHostingUrl, type FusionBranchRef, type EnsureFusionContainerResult, type SendFusionMessageResult, } from "./fusion-app.js";
|
|
104
105
|
export { sendEmail, isEmailConfigured, getEmailReadiness, getEmailProvider, type EmailAttachment, type EmailReadiness, type EmailProvider, type SendEmailArgs, } from "./email.js";
|
|
105
106
|
export { defineTransactionalEmail, defineTransactionalEmails, replaceTransactionalEmails, listTransactionalEmails, getTransactionalEmail, renderTransactionalEmailPreview, renderTransactionalEmailPreviewAsync, type TransactionalEmailDefinition, type RegisteredTransactionalEmail, } from "../email-catalog/registry.js";
|
package/dist/server/index.js
CHANGED
|
@@ -94,7 +94,7 @@ export { isServerlessRuntime } from "../db/client.js";
|
|
|
94
94
|
export { BUILDER_PUBLISH_MCP_RESOURCE, canAuthorizeBuilderApiRequest, hasBuilderApiCredentialCustody, resolveBuilderApiAuthorization, resolveBuilderLegacyRequestAuthorization, resolveBuilderRequestAuthorization, } from "./builder-api-auth.js";
|
|
95
95
|
export { BUILDER_ASSETS_WRITE_SCOPE, BUILDER_OAUTH_SCOPE, } from "./builder-oauth.js";
|
|
96
96
|
export { assertBuilderDesignSystemCodeIndexingAllowed, builderDesignSystemUrl, builderProjectBranchUrl, buildBuilderDesignSystemIndexFiles, collectBuilderDesignSystemGitHubFiles, createBuilderDesignSystemProxyFields, designSystemTierUpgradeUrl, fetchBuilderDesignSystemDecodeJobStatus, fetchBuilderDesignSystemDocs, fetchBuilderDesignSystemDocumentCount, fetchBuilderDesignSystemRecord, fetchBuilderDesignSystemTierLimit, getBuilderDesignSystemsBaseUrl, hydrateBuilderDesignSystemReference, indexBuilderDesignSystem, isBuilderDesignSystemReadyByCount, localBuilderDesignSystemId, mimeTypeForBuilderDesignSystemFilename, parseBuilderDesignSystemProxyReference, startBuilderDesignSystemIndex, startBuilderDesignSystemUpload, } from "./builder-design-systems.js";
|
|
97
|
-
export { cdnSafeOriginStatus, createBuilderProject, ensureBuilderProject, findBuilderProjectForRepo, getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, runBuilderAgent, } from "./builder-browser.js";
|
|
97
|
+
export { cdnSafeOriginStatus, createBuilderProject, ensureBuilderProject, findBuilderProjectForRepo, getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, normalizeBuilderAgentAttachments, runBuilderAgent, } from "./builder-browser.js";
|
|
98
98
|
export { ensureFusionContainer, sendFusionBranchMessage, pushFusionBranch, reserveFusionHostingSlug, deployFusionProject, getFusionDeploys, getFusionBranchEditorUrl, getFusionHostingUrl, } from "./fusion-app.js";
|
|
99
99
|
export { sendEmail, isEmailConfigured, getEmailReadiness, getEmailProvider, } from "./email.js";
|
|
100
100
|
export { defineTransactionalEmail, defineTransactionalEmails, replaceTransactionalEmails, listTransactionalEmails, getTransactionalEmail, renderTransactionalEmailPreview, renderTransactionalEmailPreviewAsync, } from "../email-catalog/registry.js";
|
|
@@ -9,3 +9,5 @@ export interface ParsedBase64DataUrl {
|
|
|
9
9
|
}
|
|
10
10
|
export declare function parseDataUrl(value: string): ParsedDataUrl | null;
|
|
11
11
|
export declare function parseBase64DataUrl(value: string): ParsedBase64DataUrl | null;
|
|
12
|
+
export declare function isBase64(data: string): boolean;
|
|
13
|
+
export declare function base64ByteLength(data: string): number;
|
package/dist/shared/data-url.js
CHANGED
|
@@ -82,3 +82,15 @@ export function parseBase64DataUrl(value) {
|
|
|
82
82
|
? { mediaType: parsed.mediaType, data: parsed.data }
|
|
83
83
|
: null;
|
|
84
84
|
}
|
|
85
|
+
export function isBase64(data) {
|
|
86
|
+
const unpadded = data.replace(/={1,2}$/, "");
|
|
87
|
+
if (!/^[A-Za-z0-9+/]*$/.test(unpadded))
|
|
88
|
+
return false;
|
|
89
|
+
return unpadded.length === data.length
|
|
90
|
+
? data.length % 4 !== 1
|
|
91
|
+
: data.length % 4 === 0;
|
|
92
|
+
}
|
|
93
|
+
export function base64ByteLength(data) {
|
|
94
|
+
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
95
|
+
return Math.floor((data.length * 3) / 4) - padding;
|
|
96
|
+
}
|
package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/attachments.md
CHANGED
|
@@ -12,7 +12,6 @@ Images and PDFs use an upload attachment with a matching base64 data URL:
|
|
|
12
12
|
contentType: "image/webp" | "image/png" | "image/jpeg" | "image/gif" | "application/pdf",
|
|
13
13
|
name: string,
|
|
14
14
|
dataUrl: "data:<contentType>;base64,<BASE64_CONTENT>",
|
|
15
|
-
size: number,
|
|
16
15
|
id: string
|
|
17
16
|
}
|
|
18
17
|
```
|
|
@@ -26,12 +25,11 @@ Text and JSON files provide decoded contents instead of a data URL:
|
|
|
26
25
|
name: string,
|
|
27
26
|
dataUrl: "",
|
|
28
27
|
text: string,
|
|
29
|
-
size: number,
|
|
30
28
|
id: string
|
|
31
29
|
}
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
Send any other text-based file (Markdown, CSV, source code, plain text) as `text/plain` with its real file name, for example `notes.md`. Use a stable or unique `id` for each attachment. A malformed attachment is rejected with `invalid_attachment` and a message naming the problem; fix that attachment and retry, because an unchanged retry fails the same way.
|
|
35
33
|
|
|
36
34
|
## Public URLs
|
|
37
35
|
|
package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md
CHANGED
|
@@ -21,14 +21,19 @@ On a browser host the host is an orchestrator, not the build environment:
|
|
|
21
21
|
(archetype, direction, first viewport, sample-data plan, agent moments).
|
|
22
22
|
2. Call `start-workspace-app-creation` on the Agent-Native Dispatch MCP
|
|
23
23
|
connector (`agent-native-dispatch`). Put the brief and the design decisions
|
|
24
|
-
in `prompt`; pass an `appId`, a short `description`, the `template
|
|
25
|
-
|
|
26
|
-
([attachments.md](attachments.md))
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
in `prompt`; pass an `appId`, a short `description`, and the `template`.
|
|
25
|
+
The tool's schema describes each argument. Send the Project's own text as
|
|
26
|
+
`attachments` ([attachments.md](attachments.md)), never as a summary: the
|
|
27
|
+
Project instructions as `project-instructions.md` and every knowledge file,
|
|
28
|
+
each verbatim as `text/plain`. Never paste whole knowledge files or binary
|
|
29
|
+
data into `prompt`. Attachments are model context
|
|
30
|
+
and are not copied into the generated workspace, so tell the Builder agent in
|
|
31
|
+
`prompt` to persist what the app needs: knowledge files as app resources, the
|
|
32
|
+
instructions as the in-app agent's instructions. If a file cannot be
|
|
33
|
+
reproduced verbatim (not in context, too large), say so in the brief and the
|
|
34
|
+
report; never swap in a digest silently. A workbook cannot be attached
|
|
35
|
+
(attachments take images, PDF, text, and JSON): pass a bounded CSV rendering
|
|
36
|
+
as text, or a public URL, and say what was left out.
|
|
32
37
|
3. Report what Dispatch actually returned: the branch, the path, and the status.
|
|
33
38
|
The browser host cannot run or inspect the app, and a returned path can 404
|
|
34
39
|
until the branch merges and deploys, so the report ends at pending or
|
package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md
CHANGED
|
@@ -20,7 +20,7 @@ heading; write `n/a` where a heading does not apply.
|
|
|
20
20
|
Source and provenance: type (thread, skill, Project, spreadsheet, export), file names, ids, what was read and what was not
|
|
21
21
|
Project goal: the user and the repeatable job, in one sentence
|
|
22
22
|
Configuration and constraints: audience, rules, output standards, approved tools
|
|
23
|
-
Knowledge sources: reference files the app ships with, by name
|
|
23
|
+
Knowledge sources: reference files the app ships with, by name
|
|
24
24
|
Repeatable workflow: the job's trigger, steps, and outcome, in the source's own words
|
|
25
25
|
Inputs and outputs: what comes in, what the user gets back, fixed output shapes
|
|
26
26
|
Judgment and review points: 1-3 agent moments, each tied to the object it acts on; what the user confirms
|
|
@@ -38,7 +38,7 @@ App design: archetype, direction, first-viewport sketch, sample-data plan
|
|
|
38
38
|
|
|
39
39
|
- Name the latest successful, repeatable job. A walkthrough of one example is
|
|
40
40
|
evidence for the job, not the job.
|
|
41
|
-
- Keep references bounded: a file name, URL,
|
|
41
|
+
- Keep references bounded: a file name, URL, or short summary
|
|
42
42
|
instead of a pasted document. No secrets, credentials, or customer data.
|
|
43
43
|
- Corrections the user made in the source are product rules. Record each one
|
|
44
44
|
as an invariant.
|