@agent-native/core 0.90.5 → 0.90.8
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/README.md +34 -7
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +20 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
- package/corpus/core/docs/content/toolkit-agent-ux.mdx +1 -1
- package/corpus/core/docs/content/toolkit-app-adapters.mdx +1 -1
- package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +1 -1
- package/corpus/core/docs/content/toolkit-collaboration.mdx +1 -1
- package/corpus/core/docs/content/toolkit-command-navigation.mdx +2 -2
- package/corpus/core/docs/content/toolkit-comments-review.mdx +1 -1
- package/corpus/core/docs/content/toolkit-history.mdx +1 -1
- package/corpus/core/docs/content/toolkit-observability.mdx +1 -1
- package/corpus/core/docs/content/toolkit-org-team.mdx +1 -1
- package/corpus/core/docs/content/toolkit-resources.mdx +1 -1
- package/corpus/core/docs/content/toolkit-settings.mdx +1 -1
- package/corpus/core/docs/content/toolkit-setup-connections.mdx +1 -1
- package/corpus/core/docs/content/toolkit-sharing-ui.mdx +1 -1
- package/corpus/core/docs/content/toolkit-sharing.mdx +1 -1
- package/corpus/core/docs/content/toolkit-shell-hooks.mdx +1 -1
- package/corpus/core/docs/content/toolkit-ui.mdx +1 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/use-db-sync.ts +99 -1
- package/corpus/core/src/collab/client.ts +49 -4
- package/corpus/core/src/deploy/build.ts +156 -0
- package/corpus/core/src/server/core-routes-plugin.ts +137 -8
- package/corpus/templates/clips/changelog/2026-07-07-faster-local-whisper-dictation-start.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +311 -31
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +2 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +9 -0
- package/corpus/templates/design/actions/read-source-file.ts +28 -0
- package/corpus/templates/design/actions/update-file.ts +76 -7
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +181 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +4 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +387 -25
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1110 -88
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +368 -16
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +24 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +27 -6
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +18 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +5 -4
- package/corpus/templates/design/app/components/design/multi-screen/handle-hit-zones.ts +124 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +23 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +19 -0
- package/corpus/templates/design/changelog/2026-07-07-dragging-a-free-floating-element-into-a-layout-column-or-row.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-dragging-a-repeated-list-item-like-a-to-do-card-generated-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-dragging-an-element-onto-a-button-or-chip-s-middle-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-elements-dropped-into-another-board-shape-now-stay-exactly-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-drag-reordering-elements-within-a-screen-no-longer-sil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-dragging-an-element-from-a-screen-onto-the-canvas-now-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-dragging-elements-between-screens-into-ai-generated-co.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-moving-elements-between-screens-no-longer-bakes-intern.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-the-cross-screen-drop-indicator-now-points-at-the-corr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-fixed-the-zoom-menu-no-longer-leaves-the-canvas-unresponsive.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-text-drawn-on-the-canvas-is-now-readable-white-instead-of-in.md +6 -0
- package/corpus/templates/design/server/source-workspace.ts +27 -3
- package/corpus/templates/design/shared/board-file.ts +359 -0
- package/corpus/templates/design/shared/breakpoint-media.ts +21 -3
- package/corpus/templates/design/shared/code-layer.ts +145 -10
- package/dist/client/use-db-sync.d.ts +18 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +88 -1
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +43 -4
- package/dist/collab/client.js.map +1 -1
- package/dist/deploy/build.d.ts +2 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +96 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/server/core-routes-plugin.d.ts +10 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +103 -9
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
- package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
- package/docs/content/toolkit-agent-ux.mdx +1 -1
- package/docs/content/toolkit-app-adapters.mdx +1 -1
- package/docs/content/toolkit-collaboration-ui.mdx +1 -1
- package/docs/content/toolkit-collaboration.mdx +1 -1
- package/docs/content/toolkit-command-navigation.mdx +2 -2
- package/docs/content/toolkit-comments-review.mdx +1 -1
- package/docs/content/toolkit-history.mdx +1 -1
- package/docs/content/toolkit-observability.mdx +1 -1
- package/docs/content/toolkit-org-team.mdx +1 -1
- package/docs/content/toolkit-resources.mdx +1 -1
- package/docs/content/toolkit-settings.mdx +1 -1
- package/docs/content/toolkit-setup-connections.mdx +1 -1
- package/docs/content/toolkit-sharing-ui.mdx +1 -1
- package/docs/content/toolkit-sharing.mdx +1 -1
- package/docs/content/toolkit-shell-hooks.mdx +1 -1
- package/docs/content/toolkit-ui.mdx +1 -1
- package/package.json +1 -1
|
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
45
46
|
summary: import("./types.js").TraceSummary;
|
|
46
47
|
spans: import("./types.js").TraceSpan[];
|
|
47
48
|
id?: undefined;
|
|
48
|
-
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
+
ok?: undefined;
|
|
51
52
|
summary?: undefined;
|
|
52
53
|
spans?: undefined;
|
|
53
54
|
id: string;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
|
@@ -54,6 +54,7 @@ interface BuilderWaitlistFormTarget {
|
|
|
54
54
|
formsOrigin: string;
|
|
55
55
|
}
|
|
56
56
|
export interface BuilderWaitlistBody {
|
|
57
|
+
email?: unknown;
|
|
57
58
|
prompt?: unknown;
|
|
58
59
|
orgName?: unknown;
|
|
59
60
|
appUrl?: unknown;
|
|
@@ -62,6 +63,15 @@ export interface BuilderWaitlistBody {
|
|
|
62
63
|
useCase?: unknown;
|
|
63
64
|
}
|
|
64
65
|
export declare function resolveFrameworkSseRoutes(sseRoute?: string): string[];
|
|
66
|
+
export declare function isAnonymousWaitlistSessionEmail(email: string): boolean;
|
|
67
|
+
export declare function resolveWaitlistEmail(sessionEmail: string | undefined, bodyEmail: unknown): string | null;
|
|
68
|
+
export declare function checkBuilderWaitlistRateLimit(event: H3Event, email: string, now?: number): {
|
|
69
|
+
ok: true;
|
|
70
|
+
} | {
|
|
71
|
+
ok: false;
|
|
72
|
+
retryAfterSeconds: number;
|
|
73
|
+
};
|
|
74
|
+
export declare function resetBuilderWaitlistRateLimitForTests(): void;
|
|
65
75
|
export declare function resolveBuilderWaitlistFormTargetForRequest(event: H3Event): BuilderWaitlistFormTarget | null;
|
|
66
76
|
export declare function buildBuilderWaitlistFormPayload(event: H3Event, sessionEmail: string, body: BuilderWaitlistBody): {
|
|
67
77
|
data: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getHeader, getCookie, setCookie, deleteCookie, getRequestURL, } from "h3";
|
|
1
|
+
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getHeader, getCookie, setCookie, deleteCookie, getRequestURL, getRequestIP, } from "h3";
|
|
2
2
|
import { readMultipartFormData } from "h3";
|
|
3
3
|
import { DEFAULT_MODEL } from "../agent/default-model.js";
|
|
4
4
|
import { registerBuiltinEngines } from "../agent/engine/builtin.js";
|
|
@@ -140,9 +140,13 @@ const BUILDER_WAITLIST_DEFAULT_USE_CASE = "builder_agent_background_coding";
|
|
|
140
140
|
const BUILDER_WAITLIST_USE_CASES = new Set([
|
|
141
141
|
BUILDER_WAITLIST_DEFAULT_USE_CASE,
|
|
142
142
|
"design_publish_app",
|
|
143
|
+
"docs_build_online_waitlist",
|
|
143
144
|
]);
|
|
144
145
|
const BUILDER_WAITLIST_FORM_TIMEOUT_MS = 8000;
|
|
145
146
|
const BUILDER_WAITLIST_TEXT_LIMIT = 4000;
|
|
147
|
+
const BUILDER_WAITLIST_RATE_LIMIT_WINDOW_MS = 60_000;
|
|
148
|
+
const BUILDER_WAITLIST_RATE_LIMIT_MAX = 5;
|
|
149
|
+
const builderWaitlistRateLimitHits = new Map();
|
|
146
150
|
export function resolveFrameworkSseRoutes(sseRoute) {
|
|
147
151
|
return Array.from(new Set([
|
|
148
152
|
sseRoute ?? FRAMEWORK_EVENTS_ROUTE,
|
|
@@ -165,6 +169,87 @@ function normalizeBuilderWaitlistUseCase(value) {
|
|
|
165
169
|
? useCase
|
|
166
170
|
: BUILDER_WAITLIST_DEFAULT_USE_CASE;
|
|
167
171
|
}
|
|
172
|
+
function isValidWaitlistEmail(email) {
|
|
173
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
174
|
+
}
|
|
175
|
+
export function isAnonymousWaitlistSessionEmail(email) {
|
|
176
|
+
return email.startsWith("anon-") && email.endsWith("@agent-native.com");
|
|
177
|
+
}
|
|
178
|
+
export function resolveWaitlistEmail(sessionEmail, bodyEmail) {
|
|
179
|
+
const provided = cleanBuilderWaitlistText(bodyEmail, 320);
|
|
180
|
+
if (provided && isValidWaitlistEmail(provided))
|
|
181
|
+
return provided;
|
|
182
|
+
if (sessionEmail && !isAnonymousWaitlistSessionEmail(sessionEmail)) {
|
|
183
|
+
return sessionEmail;
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
function normalizeWaitlistRateLimitPart(value) {
|
|
188
|
+
return value.trim().toLowerCase();
|
|
189
|
+
}
|
|
190
|
+
function getBuilderWaitlistClientIp(event) {
|
|
191
|
+
const trusted = getHeader(event, "x-nf-client-connection-ip") ??
|
|
192
|
+
getHeader(event, "cf-connecting-ip") ??
|
|
193
|
+
getHeader(event, "true-client-ip") ??
|
|
194
|
+
getHeader(event, "x-real-ip");
|
|
195
|
+
if (trusted && trusted.trim())
|
|
196
|
+
return trusted.trim();
|
|
197
|
+
const forwardedFor = getHeader(event, "x-forwarded-for");
|
|
198
|
+
const forwardedClientIp = forwardedFor?.split(",")[0]?.trim();
|
|
199
|
+
if (forwardedClientIp)
|
|
200
|
+
return forwardedClientIp;
|
|
201
|
+
try {
|
|
202
|
+
return getRequestIP(event) ?? undefined;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function getBuilderWaitlistRateLimitKeys(event, email) {
|
|
209
|
+
const clientIp = getBuilderWaitlistClientIp(event);
|
|
210
|
+
return [
|
|
211
|
+
`email:${normalizeWaitlistRateLimitPart(email)}`,
|
|
212
|
+
`ip:${normalizeWaitlistRateLimitPart(clientIp ?? "unknown")}`,
|
|
213
|
+
];
|
|
214
|
+
}
|
|
215
|
+
export function checkBuilderWaitlistRateLimit(event, email, now = Date.now()) {
|
|
216
|
+
const keys = getBuilderWaitlistRateLimitKeys(event, email);
|
|
217
|
+
let retryAfterMs = 0;
|
|
218
|
+
for (const key of keys) {
|
|
219
|
+
const entry = builderWaitlistRateLimitHits.get(key);
|
|
220
|
+
if (!entry)
|
|
221
|
+
continue;
|
|
222
|
+
if (entry.resetAt <= now) {
|
|
223
|
+
builderWaitlistRateLimitHits.delete(key);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (entry.count >= BUILDER_WAITLIST_RATE_LIMIT_MAX) {
|
|
227
|
+
retryAfterMs = Math.max(retryAfterMs, entry.resetAt - now);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (retryAfterMs > 0) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
retryAfterSeconds: Math.max(1, Math.ceil(retryAfterMs / 1000)),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
for (const key of keys) {
|
|
237
|
+
const entry = builderWaitlistRateLimitHits.get(key);
|
|
238
|
+
if (!entry || entry.resetAt <= now) {
|
|
239
|
+
builderWaitlistRateLimitHits.set(key, {
|
|
240
|
+
count: 1,
|
|
241
|
+
resetAt: now + BUILDER_WAITLIST_RATE_LIMIT_WINDOW_MS,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
entry.count += 1;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { ok: true };
|
|
249
|
+
}
|
|
250
|
+
export function resetBuilderWaitlistRateLimitForTests() {
|
|
251
|
+
builderWaitlistRateLimitHits.clear();
|
|
252
|
+
}
|
|
168
253
|
function normalizeHttpOrigin(value) {
|
|
169
254
|
try {
|
|
170
255
|
const url = new URL(value);
|
|
@@ -1318,21 +1403,30 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1318
1403
|
return { error: "Method not allowed" };
|
|
1319
1404
|
}
|
|
1320
1405
|
const session = await getSession(event).catch(() => null);
|
|
1321
|
-
if (!session?.email) {
|
|
1322
|
-
setResponseStatus(event, 401);
|
|
1323
|
-
return { error: "Authentication required" };
|
|
1324
|
-
}
|
|
1325
1406
|
const body = ((await readBody(event).catch(() => ({}))) ??
|
|
1326
1407
|
{});
|
|
1327
|
-
const
|
|
1408
|
+
const waitlistEmail = resolveWaitlistEmail(session?.email, body.email);
|
|
1409
|
+
if (!waitlistEmail) {
|
|
1410
|
+
setResponseStatus(event, 400);
|
|
1411
|
+
return { error: "Valid email required" };
|
|
1412
|
+
}
|
|
1413
|
+
const waitlistRateLimit = checkBuilderWaitlistRateLimit(event, waitlistEmail);
|
|
1414
|
+
if (!waitlistRateLimit.ok) {
|
|
1415
|
+
setResponseStatus(event, 429);
|
|
1416
|
+
setResponseHeader(event, "Retry-After", String(waitlistRateLimit.retryAfterSeconds));
|
|
1417
|
+
return {
|
|
1418
|
+
error: "Too many waitlist requests. Please try again in a minute.",
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
const waitlistPayload = buildBuilderWaitlistFormPayload(event, waitlistEmail, body);
|
|
1328
1422
|
const waitlistSource = waitlistPayload.data.source;
|
|
1329
1423
|
const waitlistUseCase = waitlistPayload.data.useCase;
|
|
1330
1424
|
let formSubmission;
|
|
1331
1425
|
try {
|
|
1332
|
-
formSubmission = await submitBuilderWaitlistForm(event,
|
|
1426
|
+
formSubmission = await submitBuilderWaitlistForm(event, waitlistEmail, body);
|
|
1333
1427
|
}
|
|
1334
1428
|
catch (err) {
|
|
1335
|
-
await trackBuilderLifecycle(event, "builder branch waitlist form failed",
|
|
1429
|
+
await trackBuilderLifecycle(event, "builder branch waitlist form failed", waitlistEmail, {
|
|
1336
1430
|
reason: err instanceof Error ? err.message : "unknown_waitlist_error",
|
|
1337
1431
|
source: waitlistSource,
|
|
1338
1432
|
stage: "waitlist",
|
|
@@ -1343,7 +1437,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1343
1437
|
error: "Couldn't join the waitlist. Please try again in a moment.",
|
|
1344
1438
|
};
|
|
1345
1439
|
}
|
|
1346
|
-
await trackBuilderLifecycle(event, "builder branch waitlist joined",
|
|
1440
|
+
await trackBuilderLifecycle(event, "builder branch waitlist joined", waitlistEmail, {
|
|
1347
1441
|
formId: formSubmission.formId ?? null,
|
|
1348
1442
|
formSubmitted: formSubmission.submitted,
|
|
1349
1443
|
source: waitlistSource,
|