@agent-native/core 0.114.2 → 0.114.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +24 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/db/request-telemetry.ts +32 -8
  5. package/corpus/core/src/settings/store.ts +15 -6
  6. package/corpus/core/src/shared/optional-node-builtins.ts +66 -0
  7. package/corpus/core/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  8. package/corpus/core/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  9. package/corpus/core/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  10. package/corpus/core/src/vite/client.ts +5 -0
  11. package/corpus/templates/analytics/.agents/skills/feature-flags/SKILL.md +1 -1
  12. package/corpus/templates/assets/.agents/skills/feature-flags/SKILL.md +1 -1
  13. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +2 -2
  14. package/corpus/templates/brain/.agents/skills/feature-flags/SKILL.md +1 -1
  15. package/corpus/templates/calendar/.agents/skills/feature-flags/SKILL.md +1 -1
  16. package/corpus/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  17. package/corpus/templates/clips/.agents/skills/feature-flags/SKILL.md +1 -1
  18. package/corpus/templates/clips/shared/feature-flags.ts +1 -1
  19. package/corpus/templates/content/.agents/skills/feature-flags/SKILL.md +1 -1
  20. package/corpus/templates/design/.agents/skills/feature-flags/SKILL.md +1 -1
  21. package/corpus/templates/design/actions/update-file.ts +10 -1
  22. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +30 -1
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +14 -1
  24. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +547 -50
  25. package/corpus/templates/design/app/components/design/dnd-debug.ts +27 -0
  26. package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +19 -2
  27. package/corpus/templates/design/app/i18n-data.ts +19 -0
  28. package/corpus/templates/design/app/lib/design-save-outbox.ts +46 -2
  29. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +4 -9
  30. package/corpus/templates/design/app/pages/design-editor/history.ts +14 -1
  31. package/corpus/templates/design/changelog/2026-07-17-drawn-shapes-stay-free-positioned-when-dragged.md +6 -0
  32. package/corpus/templates/design/changelog/2026-07-17-shapes-show-a-live-outline-while-you-draw.md +6 -0
  33. package/corpus/templates/design/shared/drag-reflow.ts +375 -0
  34. package/corpus/templates/design/shared/full-app.ts +1 -1
  35. package/corpus/templates/dispatch/.agents/skills/feature-flags/SKILL.md +1 -1
  36. package/corpus/templates/forms/.agents/skills/feature-flags/SKILL.md +1 -1
  37. package/corpus/templates/macros/.agents/skills/feature-flags/SKILL.md +1 -1
  38. package/corpus/templates/mail/.agents/skills/feature-flags/SKILL.md +1 -1
  39. package/corpus/templates/plan/.agents/skills/feature-flags/SKILL.md +1 -1
  40. package/corpus/templates/slides/.agents/skills/feature-flags/SKILL.md +1 -1
  41. package/corpus/templates/tasks/.agents/skills/feature-flags/SKILL.md +1 -1
  42. package/dist/collab/routes.d.ts +1 -1
  43. package/dist/collab/struct-routes.d.ts +1 -1
  44. package/dist/db/request-telemetry.d.ts.map +1 -1
  45. package/dist/db/request-telemetry.js +24 -6
  46. package/dist/db/request-telemetry.js.map +1 -1
  47. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  48. package/dist/notifications/routes.d.ts +1 -1
  49. package/dist/observability/routes.d.ts +3 -3
  50. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  51. package/dist/server/transcribe-voice.d.ts +1 -1
  52. package/dist/settings/store.d.ts +1 -1
  53. package/dist/settings/store.d.ts.map +1 -1
  54. package/dist/settings/store.js +14 -6
  55. package/dist/settings/store.js.map +1 -1
  56. package/dist/shared/optional-node-builtins.d.ts +23 -0
  57. package/dist/shared/optional-node-builtins.d.ts.map +1 -0
  58. package/dist/shared/optional-node-builtins.js +35 -0
  59. package/dist/shared/optional-node-builtins.js.map +1 -0
  60. package/dist/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  61. package/dist/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  62. package/dist/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  63. package/dist/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  64. package/dist/vite/client.d.ts.map +1 -1
  65. package/dist/vite/client.js +2 -0
  66. package/dist/vite/client.js.map +1 -1
  67. package/package.json +2 -1
  68. package/src/db/request-telemetry.ts +32 -8
  69. package/src/settings/store.ts +15 -6
  70. package/src/shared/optional-node-builtins.ts +66 -0
  71. package/src/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
  72. package/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
  73. package/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
  74. package/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
  75. package/src/vite/client.ts +5 -0
package/corpus/README.md CHANGED
@@ -28,6 +28,6 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
 
29
29
  ## Generated Counts
30
30
 
31
- - core files: 1516
31
+ - core files: 1517
32
32
  - toolkit files: 137
33
- - template files: 6101
33
+ - template files: 6105
@@ -1,5 +1,29 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.114.3
4
+
5
+ ### Patch Changes
6
+
7
+ - c47eb50: fix(core): keep feature-flag definitions out of the browser server graph
8
+
9
+ App-shared config that imported feature-flag definitions from
10
+ `@agent-native/core/feature-flags` pulled the barrel's server re-exports
11
+ (`store` → `settings/store` → `db/client` → request telemetry) into the client
12
+ dev graph. Vite's dev server does not tree-shake, so the browser evaluated that
13
+ server chain and `request-telemetry`'s top-level `new AsyncLocalStorage()` threw
14
+ against the externalized `node:async_hooks` stub, breaking app load in dev
15
+ (production tree-shakes it away and was unaffected).
16
+ - Add a client-safe `@agent-native/core/feature-flags/registry` entry for
17
+ `defineFeatureFlag` / `defineFeatureFlags` / `registerFeatureFlags` so shared
18
+ config no longer imports the server barrel.
19
+ - Make `db/request-telemetry` and `settings/store` resolve `AsyncLocalStorage` /
20
+ `EventEmitter` lazily via `process.getBuiltinModule` (matching
21
+ `server/request-context`) instead of a top-level value import, so the modules
22
+ can be evaluated in any runtime without tripping the browser stub.
23
+ - Add a regression guard asserting the client-safe registry entry never reaches
24
+ the server layer and that those modules never statically value-import the
25
+ builtins.
26
+
3
27
  ## 0.114.2
4
28
 
5
29
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.114.2",
3
+ "version": "0.114.3",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -159,6 +159,7 @@
159
159
  "./application-state": "./dist/application-state/index.js",
160
160
  "./settings": "./dist/settings/index.js",
161
161
  "./feature-flags": "./dist/feature-flags/index.js",
162
+ "./feature-flags/registry": "./dist/feature-flags/registry.js",
162
163
  "./feature-flags/actions/get-feature-flags": "./dist/feature-flags/actions/get-feature-flags.js",
163
164
  "./feature-flags/actions/list-feature-flags": "./dist/feature-flags/actions/list-feature-flags.js",
164
165
  "./feature-flags/actions/set-feature-flag": "./dist/feature-flags/actions/set-feature-flag.js",
@@ -1,4 +1,4 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
1
+ import { getAsyncLocalStorageCtor } from "../shared/optional-node-builtins.js";
2
2
 
3
3
  export type DatabaseOperationKind = "connect" | "query";
4
4
 
@@ -30,15 +30,39 @@ interface StartupDatabaseTelemetryState {
30
30
  claimed: boolean;
31
31
  telemetry: DatabaseRequestTelemetry;
32
32
  }
33
+ interface TelemetryStorage {
34
+ getStore(): DatabaseRequestTelemetry | undefined;
35
+ run<T>(store: DatabaseRequestTelemetry, fn: () => T): T;
36
+ enterWith(store: DatabaseRequestTelemetry): void;
37
+ }
38
+
33
39
  type GlobalWithDatabaseTelemetry = typeof globalThis & {
34
- [STORAGE_KEY]?: AsyncLocalStorage<DatabaseRequestTelemetry>;
40
+ [STORAGE_KEY]?: TelemetryStorage;
35
41
  [STARTUP_STATE_KEY]?: StartupDatabaseTelemetryState;
36
42
  };
37
43
 
38
44
  const globalRef = globalThis as GlobalWithDatabaseTelemetry;
39
- const storage =
40
- globalRef[STORAGE_KEY] ??
41
- (globalRef[STORAGE_KEY] = new AsyncLocalStorage<DatabaseRequestTelemetry>());
45
+
46
+ // AsyncLocalStorage is resolved lazily, never at module load: this module can
47
+ // land in the browser dev graph, where a top-level `new AsyncLocalStorage()`
48
+ // would throw against Vite's externalized `node:async_hooks` stub. On non-Node
49
+ // runtimes telemetry no-ops.
50
+ const NOOP_STORAGE: TelemetryStorage = {
51
+ getStore: () => undefined,
52
+ run: (_store, fn) => fn(),
53
+ enterWith: () => {},
54
+ };
55
+
56
+ function getStorage(): TelemetryStorage {
57
+ const existing = globalRef[STORAGE_KEY];
58
+ if (existing) return existing;
59
+ const Ctor = getAsyncLocalStorageCtor();
60
+ const created: TelemetryStorage = Ctor
61
+ ? new Ctor<DatabaseRequestTelemetry>()
62
+ : NOOP_STORAGE;
63
+ globalRef[STORAGE_KEY] = created;
64
+ return created;
65
+ }
42
66
 
43
67
  export function createDatabaseRequestTelemetry(): DatabaseRequestTelemetry {
44
68
  return {
@@ -66,7 +90,7 @@ const startupState =
66
90
  });
67
91
 
68
92
  function currentDatabaseTelemetry(): DatabaseRequestTelemetry | undefined {
69
- const requestTelemetry = storage.getStore();
93
+ const requestTelemetry = getStorage().getStore();
70
94
  if (requestTelemetry) return requestTelemetry;
71
95
  if (!startupState.claimed && Date.now() <= startupState.captureUntil) {
72
96
  return startupState.telemetry;
@@ -86,13 +110,13 @@ export function runWithDatabaseRequestTelemetry<T>(
86
110
  telemetry: DatabaseRequestTelemetry,
87
111
  fn: () => T,
88
112
  ): T {
89
- return storage.run(telemetry, fn);
113
+ return getStorage().run(telemetry, fn);
90
114
  }
91
115
 
92
116
  export function enterDatabaseRequestTelemetry(
93
117
  telemetry: DatabaseRequestTelemetry,
94
118
  ): void {
95
- storage.enterWith(telemetry);
119
+ getStorage().enterWith(telemetry);
96
120
  }
97
121
 
98
122
  export function beginDatabaseOperation(
@@ -1,9 +1,10 @@
1
- import { EventEmitter } from "events";
1
+ import type { EventEmitter } from "node:events";
2
2
 
3
3
  import { getDbExec, isPostgres, intType } from "../db/client.js";
4
4
  import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
5
5
  import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
6
6
  import { getRequestContext } from "../server/request-context.js";
7
+ import { createEventEmitter } from "../shared/optional-node-builtins.js";
7
8
 
8
9
  let _initPromise: Promise<void> | undefined;
9
10
 
@@ -29,12 +30,20 @@ function requestSettingsCache(): Map<string, string | null> | null {
29
30
  return cache;
30
31
  }
31
32
 
32
- const _emitter = new EventEmitter();
33
+ // Created lazily so this module can be evaluated in the browser dev graph
34
+ // without a top-level `new EventEmitter()` tripping Vite's externalized
35
+ // `node:events` stub. The emitter drives server-side SSE fan-out only.
36
+ let _emitter: EventEmitter | undefined;
33
37
 
34
- export function getSettingsEmitter(): EventEmitter {
38
+ function settingsEmitter(): EventEmitter {
39
+ if (!_emitter) _emitter = createEventEmitter();
35
40
  return _emitter;
36
41
  }
37
42
 
43
+ export function getSettingsEmitter(): EventEmitter {
44
+ return settingsEmitter();
45
+ }
46
+
38
47
  function settingsTable(): string {
39
48
  return isPostgres() ? "public.settings" : "settings";
40
49
  }
@@ -174,7 +183,7 @@ export async function mutateSetting(
174
183
  });
175
184
  if (result.rowsAffected === 0) continue;
176
185
  requestSettingsCache()?.set(key, nextRaw);
177
- _emitter.emit("settings", {
186
+ settingsEmitter().emit("settings", {
178
187
  source: "settings",
179
188
  type: "change",
180
189
  key,
@@ -200,7 +209,7 @@ export async function putSetting(
200
209
  args: [key, JSON.stringify(value), Date.now()],
201
210
  });
202
211
  requestSettingsCache()?.set(key, JSON.stringify(value));
203
- _emitter.emit("settings", {
212
+ settingsEmitter().emit("settings", {
204
213
  source: "settings",
205
214
  type: "change",
206
215
  key,
@@ -221,7 +230,7 @@ export async function deleteSetting(
221
230
  });
222
231
  requestSettingsCache()?.set(key, null);
223
232
  if (result.rowsAffected > 0) {
224
- _emitter.emit("settings", {
233
+ settingsEmitter().emit("settings", {
225
234
  source: "settings",
226
235
  type: "delete",
227
236
  key,
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Browser-safe accessors for optional Node builtins.
3
+ *
4
+ * `node:async_hooks` and `node:events` are server-only, but the modules that
5
+ * use them (db telemetry, settings/resource emitters) can land in the browser
6
+ * dev graph before tree-shaking runs. A static `import { X } from "node:..."`
7
+ * resolves to Vite's externalized stub and throws the instant the value is
8
+ * touched at module load. We read the builtin through `process.getBuiltinModule`
9
+ * instead — the same guard `server/request-context.ts` uses — so there is no
10
+ * static `node:` import to evaluate in the browser, and callers fall back to a
11
+ * no-op on non-Node runtimes (browser, or Node without getBuiltinModule).
12
+ */
13
+ import type { AsyncLocalStorage } from "node:async_hooks";
14
+ import type { EventEmitter } from "node:events";
15
+
16
+ type BuiltinProcess = {
17
+ versions?: { node?: string };
18
+ getBuiltinModule?: (name: string) => unknown;
19
+ };
20
+
21
+ function nodeBuiltin<T>(name: string): T | undefined {
22
+ const proc =
23
+ typeof process === "undefined" ? undefined : (process as BuiltinProcess);
24
+ if (
25
+ typeof window !== "undefined" ||
26
+ !proc ||
27
+ !proc.versions?.node ||
28
+ typeof proc.getBuiltinModule !== "function"
29
+ ) {
30
+ return undefined;
31
+ }
32
+ return proc.getBuiltinModule(name) as T | undefined;
33
+ }
34
+
35
+ export type AsyncLocalStorageCtor = new <T>() => AsyncLocalStorage<T>;
36
+
37
+ export function getAsyncLocalStorageCtor(): AsyncLocalStorageCtor | undefined {
38
+ return nodeBuiltin<{ AsyncLocalStorage: AsyncLocalStorageCtor }>(
39
+ "node:async_hooks",
40
+ )?.AsyncLocalStorage;
41
+ }
42
+
43
+ type EventEmitterCtor = new () => EventEmitter;
44
+
45
+ /**
46
+ * Returns a real Node EventEmitter on Node, or a no-op emitter elsewhere. The
47
+ * emitters that use this drive server-side SSE fan-out; nothing subscribes in
48
+ * the browser, so the no-op fallback is inert rather than wrong.
49
+ */
50
+ export function createEventEmitter(): EventEmitter {
51
+ const Ctor = nodeBuiltin<{ EventEmitter: EventEmitterCtor }>(
52
+ "node:events",
53
+ )?.EventEmitter;
54
+ if (Ctor) return new Ctor();
55
+ const noop = {
56
+ on: () => noop,
57
+ off: () => noop,
58
+ once: () => noop,
59
+ addListener: () => noop,
60
+ removeListener: () => noop,
61
+ removeAllListeners: () => noop,
62
+ setMaxListeners: () => noop,
63
+ emit: () => false,
64
+ };
65
+ return noop as unknown as EventEmitter;
66
+ }
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -805,6 +805,7 @@ const CORE_CLIENT_SUBPATHS = [
805
805
  "@agent-native/core/client/components/RemoteSelectionRings",
806
806
  "@agent-native/core/client/visual-style-controls",
807
807
  "@agent-native/core/client/feature-flags",
808
+ "@agent-native/core/feature-flags/registry",
808
809
  "@agent-native/core/client/hooks",
809
810
  "@agent-native/core/client/host",
810
811
  "@agent-native/core/client/i18n",
@@ -1158,6 +1159,10 @@ function getCoreSourceAliases(
1158
1159
  coreSrc,
1159
1160
  "client/feature-flags/index.ts",
1160
1161
  ),
1162
+ "@agent-native/core/feature-flags/registry": path.join(
1163
+ coreSrc,
1164
+ "feature-flags/registry.ts",
1165
+ ),
1161
1166
  "@agent-native/core/client/hooks": path.join(
1162
1167
  coreSrc,
1163
1168
  "client/hooks/index.ts",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -2950,7 +2950,7 @@ function AssetCardsView({ items }: { items: LaneGalleryItem[] }) {
2950
2950
  aria-label={t("library.selectAsset", {
2951
2951
  title: item.title,
2952
2952
  })}
2953
- className="border-background bg-background/90 shadow-sm"
2953
+ className="border-2 border-foreground/40 bg-background/90 shadow-sm"
2954
2954
  />
2955
2955
  ) : null}
2956
2956
  </div>
@@ -3503,7 +3503,7 @@ function AssetLaneTile({
3503
3503
  onCheckedChange={(checked) => onToggle(checked === true)}
3504
3504
  aria-label={t("library.selectAsset", { title: displayTitle })}
3505
3505
  className={[
3506
- "border-background bg-background/90 shadow-sm opacity-100 transition sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100",
3506
+ "border-2 border-foreground/40 bg-background/90 shadow-sm opacity-100 transition sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100",
3507
3507
  selected ? "sm:opacity-100" : "",
3508
3508
  ].join(" ")}
3509
3509
  />
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineFeatureFlag,
3
3
  defineFeatureFlags,
4
- } from "@agent-native/core/feature-flags";
4
+ } from "@agent-native/core/feature-flags/registry";
5
5
 
6
6
  /**
7
7
  * Desktop app: use the custom ScreenCaptureKit (SCK) capture pipeline
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -37,7 +37,7 @@ Keep definitions in a shared TypeScript module so server and client code use the
37
37
  same stable key. Flags are boolean and default-off.
38
38
 
39
39
  ```ts
40
- import { defineFeatureFlag } from "@agent-native/core/feature-flags";
40
+ import { defineFeatureFlag } from "@agent-native/core/feature-flags/registry";
41
41
 
42
42
  export const FULL_APP_BUILDING = defineFeatureFlag({
43
43
  key: "full-app-building",
@@ -169,7 +169,16 @@ export default defineAction({
169
169
  .limit(1);
170
170
 
171
171
  if (!file) {
172
- throw new Error(`File not found: ${id}`);
172
+ // Terminal (not transient): the row is gone or out of access scope, so a
173
+ // retry can never succeed. Mark it 404 so the client save-outbox drops the
174
+ // entry instead of retrying forever (which turns one orphaned/deleted
175
+ // screen into an update-file 500 storm — see design-save-outbox
176
+ // isTerminalSaveError).
177
+ const notFound = new Error(`File not found: ${id}`) as Error & {
178
+ status?: number;
179
+ };
180
+ notFound.status = 404;
181
+ throw notFound;
173
182
  }
174
183
 
175
184
  await assertAccess("design", file.designId, "editor");
@@ -109,6 +109,7 @@ import {
109
109
  schedulePendingTextEditActivation,
110
110
  } from "./design-canvas/pending-text-edit";
111
111
  import { DeviceFrame } from "./DeviceFrame";
112
+ import { dndHostLog } from "./dnd-debug";
112
113
  import { shapeClosingHandles } from "./multi-screen/draft-primitives";
113
114
  import type {
114
115
  ElementInfo,
@@ -304,6 +305,15 @@ ${editorChromeBridgeScript}
304
305
  </script>
305
306
  `;
306
307
 
308
+ /**
309
+ * Master switch for the Figma-parity live-reflow drag (hysteresis-stabilized
310
+ * targeting + size guard in Phase 0; transform lift/follow, live sibling
311
+ * reflow, and exact absolute commit in Phase 1). Flip to `true` to try the new
312
+ * drag feel; flip back to `false` for the previous behavior without a revert.
313
+ * Baked into the injected bridge as `__LIVE_REFLOW_ENABLED__`.
314
+ */
315
+ const LIVE_REFLOW_ENABLED = true;
316
+
307
317
  interface DesignCanvasProps {
308
318
  content: string;
309
319
  contentKey?: string;
@@ -931,6 +941,10 @@ function buildEditorChromeBridgeScript(args: {
931
941
  "__RUNTIME_LAYER_SNAPSHOT_ENABLED__",
932
942
  args.runtimeLayerSnapshotEnabled ? "true" : "false",
933
943
  )
944
+ .replace(
945
+ "__LIVE_REFLOW_ENABLED__",
946
+ LIVE_REFLOW_ENABLED ? "true" : "false",
947
+ )
934
948
  );
935
949
  }
936
950
 
@@ -2106,7 +2120,11 @@ export function DesignCanvas({
2106
2120
  "__DESIGN_CANVAS_CONTENT_OFFSET_Y__",
2107
2121
  String(Math.round(embeddedFrame?.contentOffsetY ?? 0)),
2108
2122
  )
2109
- .replace("__RUNTIME_LAYER_SNAPSHOT_ENABLED__", "false");
2123
+ .replace("__RUNTIME_LAYER_SNAPSHOT_ENABLED__", "false")
2124
+ .replace(
2125
+ "__LIVE_REFLOW_ENABLED__",
2126
+ LIVE_REFLOW_ENABLED ? "true" : "false",
2127
+ );
2110
2128
  // ALWAYS injected (like the other always-on bridges above) so
2111
2129
  // MultiScreenCanvas's cross-screen drag hit-testing
2112
2130
  // (agent-native:hit-test / agent-native:hit-test-result) resolves an
@@ -2479,6 +2497,12 @@ export function DesignCanvas({
2479
2497
  const selector = String(e.data.selector || "");
2480
2498
  const anchorSelector = String(e.data.anchorSelector || "");
2481
2499
  const placement = String(e.data.placement || "after");
2500
+ dndHostLog("recv:structure-change", {
2501
+ selector,
2502
+ anchorSelector,
2503
+ placement,
2504
+ dropMode: e.data.dropMode,
2505
+ });
2482
2506
  const requestId =
2483
2507
  typeof e.data.requestId === "string" ? e.data.requestId : undefined;
2484
2508
  const sourceId =
@@ -2546,6 +2570,11 @@ export function DesignCanvas({
2546
2570
  : undefined,
2547
2571
  },
2548
2572
  );
2573
+ dndHostLog("persist:result", {
2574
+ applied,
2575
+ requestId,
2576
+ willAck: Boolean(requestId) && applied !== "pending",
2577
+ });
2549
2578
  if (requestId && applied !== "pending") {
2550
2579
  iframeRef.current?.contentWindow?.postMessage(
2551
2580
  {
@@ -84,6 +84,7 @@ import {
84
84
  } from "./canvas-primitive-style";
85
85
  import { appendHitTestResponder } from "./design-canvas/hit-test";
86
86
  import { DesignCanvas } from "./DesignCanvas";
87
+ import { dndHostLog } from "./dnd-debug";
87
88
  import {
88
89
  gradientToCss,
89
90
  parseGradientCss,
@@ -154,6 +155,10 @@ const TRANSFORM_BADGE_MAX_WIDTH = 180;
154
155
  // (frame z-order is a small per-design integer) while staying well under the
155
156
  // reserved resize-handle stacking range (999_999+).
156
157
  const TOP_SCREEN_Z_BOOST = 100_000;
158
+ // A live draw preview must paint above every screen — including the
159
+ // TOP_SCREEN_Z_BOOSTed active one — or its outline hides behind the opaque
160
+ // screen iframe until commit. Stays below the resize-handle range (999_999+).
161
+ const DRAFT_PREVIEW_Z = TOP_SCREEN_Z_BOOST + 50_000;
157
162
  const EMPTY_SELECTED_LAYER_SELECTOR_GROUPS_BY_SCREEN: Record<
158
163
  string,
159
164
  string[][]
@@ -2269,6 +2274,13 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
2269
2274
  return;
2270
2275
  }
2271
2276
 
2277
+ if (msg.phase !== "move") {
2278
+ dndHostLog("overview:cross-screen", {
2279
+ phase: msg.phase,
2280
+ source: sourceScreenId,
2281
+ selector: msg.selector,
2282
+ });
2283
+ }
2272
2284
  if (msg.phase === "start") {
2273
2285
  setCrossScreenSourceIsBoard(sourceScreenId === boardFileId);
2274
2286
  crossScreenDragMsgRef.current = {
@@ -5167,6 +5179,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
5167
5179
  frameGeometryWithOverrides(after, state.originFrames),
5168
5180
  after,
5169
5181
  );
5182
+ dndHostLog("overview:frame-commit", { ids: state.targetIds });
5170
5183
  suppressNextPick.current = true;
5171
5184
  }
5172
5185
  finishDrag();
@@ -8156,7 +8169,7 @@ function DraftPrimitiveLayer({
8156
8169
  ...frameStyleLeftTop(geometry),
8157
8170
  width: geometry.width,
8158
8171
  height: geometry.height,
8159
- zIndex: geometry.z ?? 40,
8172
+ zIndex: preview ? DRAFT_PREVIEW_Z : (geometry.z ?? 40),
8160
8173
  transform: geometry.rotation
8161
8174
  ? `rotate(${geometry.rotation}deg)`
8162
8175
  : undefined,