@agent-native/core 0.114.1 → 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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +30 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/cli/create.ts +23 -0
- package/corpus/core/src/cli/skills.ts +24 -0
- package/corpus/core/src/db/request-telemetry.ts +32 -8
- package/corpus/core/src/settings/store.ts +15 -6
- package/corpus/core/src/shared/optional-node-builtins.ts +66 -0
- package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/core/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/core/src/templates/default/AGENTS.md +4 -1
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/core/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/core/src/templates/headless/AGENTS.md +5 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/AGENTS.md +3 -1
- package/corpus/core/src/templates/workspace-root/AGENTS.md +3 -1
- package/corpus/core/src/vite/client.ts +5 -0
- package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/analytics/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/analytics/AGENTS.md +5 -0
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/assets/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/assets/AGENTS.md +5 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +2 -2
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/brain/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/brain/AGENTS.md +5 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/calendar/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/calendar/AGENTS.md +5 -0
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/chat/AGENTS.md +4 -1
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/clips/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/clips/AGENTS.md +5 -0
- package/corpus/templates/clips/shared/feature-flags.ts +1 -1
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/content/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/content/AGENTS.md +5 -0
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/design/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/design/AGENTS.md +5 -0
- package/corpus/templates/design/actions/update-file.ts +10 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +30 -1
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +14 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +547 -50
- package/corpus/templates/design/app/components/design/dnd-debug.ts +27 -0
- package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +19 -2
- package/corpus/templates/design/app/i18n-data.ts +19 -0
- package/corpus/templates/design/app/lib/design-save-outbox.ts +46 -2
- package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +4 -9
- package/corpus/templates/design/app/pages/design-editor/history.ts +14 -1
- package/corpus/templates/design/changelog/2026-07-17-drawn-shapes-stay-free-positioned-when-dragged.md +6 -0
- package/corpus/templates/design/changelog/2026-07-17-shapes-show-a-live-outline-while-you-draw.md +6 -0
- package/corpus/templates/design/shared/drag-reflow.ts +375 -0
- package/corpus/templates/design/shared/full-app.ts +1 -1
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/dispatch/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/dispatch/AGENTS.md +5 -0
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/forms/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/forms/AGENTS.md +5 -0
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/macros/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/macros/AGENTS.md +5 -0
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/mail/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/mail/AGENTS.md +5 -0
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/plan/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/plan/AGENTS.md +5 -0
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/slides/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/slides/AGENTS.md +5 -0
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/corpus/templates/tasks/.agents/skills/feature-flags/SKILL.md +1 -1
- package/corpus/templates/tasks/AGENTS.md +5 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +21 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +19 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/request-telemetry.d.ts.map +1 -1
- package/dist/db/request-telemetry.js +24 -6
- package/dist/db/request-telemetry.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/settings/store.d.ts +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +14 -6
- package/dist/settings/store.js.map +1 -1
- package/dist/shared/optional-node-builtins.d.ts +23 -0
- package/dist/shared/optional-node-builtins.d.ts.map +1 -0
- package/dist/shared/optional-node-builtins.js +35 -0
- package/dist/shared/optional-node-builtins.js.map +1 -0
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/dist/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
- package/dist/templates/chat/AGENTS.md +4 -1
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/dist/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
- package/dist/templates/default/AGENTS.md +4 -1
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/dist/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
- package/dist/templates/headless/AGENTS.md +5 -1
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
- package/dist/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
- package/dist/templates/workspace-core/AGENTS.md +3 -1
- package/dist/templates/workspace-root/AGENTS.md +3 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/create.ts +23 -0
- package/src/cli/skills.ts +24 -0
- package/src/db/request-telemetry.ts +32 -8
- package/src/settings/store.ts +15 -6
- package/src/shared/optional-node-builtins.ts +66 -0
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/src/templates/chat/.agents/skills/feature-flags/SKILL.md +1 -1
- package/src/templates/chat/AGENTS.md +4 -1
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/src/templates/default/.agents/skills/feature-flags/SKILL.md +1 -1
- package/src/templates/default/AGENTS.md +4 -1
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
- package/src/templates/headless/.agents/skills/feature-flags/SKILL.md +1 -1
- package/src/templates/headless/AGENTS.md +5 -1
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
- package/src/templates/workspace-core/.agents/skills/feature-flags/SKILL.md +1 -1
- package/src/templates/workspace-core/AGENTS.md +3 -1
- package/src/templates/workspace-root/AGENTS.md +3 -1
- package/src/vite/client.ts +5 -0
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-telemetry.d.ts","sourceRoot":"","sources":["../../src/db/request-telemetry.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"request-telemetry.d.ts","sourceRoot":"","sources":["../../src/db/request-telemetry.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAgDD,wBAAgB,8BAA8B,IAAI,wBAAwB,CAezE;AAmBD,wBAAgB,6BAA6B,IACzC,wBAAwB,GACxB,SAAS,CAIZ;AAED,wBAAgB,+BAA+B,CAAC,CAAC,EAC/C,SAAS,EAAE,wBAAwB,EACnC,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAEH;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,wBAAwB,GAClC,IAAI,CAEN;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,qBAAqB,GAC/B,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,KAAK,IAAI,CAiDpD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
|
|
@@ -1,10 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAsyncLocalStorageCtor } from "../shared/optional-node-builtins.js";
|
|
2
2
|
const STORAGE_KEY = Symbol.for("@agent-native/core/db.request-telemetry-storage");
|
|
3
3
|
const STARTUP_STATE_KEY = Symbol.for("@agent-native/core/db.startup-telemetry-state");
|
|
4
4
|
const STARTUP_CAPTURE_WINDOW_MS = 120_000;
|
|
5
5
|
const globalRef = globalThis;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// AsyncLocalStorage is resolved lazily, never at module load: this module can
|
|
7
|
+
// land in the browser dev graph, where a top-level `new AsyncLocalStorage()`
|
|
8
|
+
// would throw against Vite's externalized `node:async_hooks` stub. On non-Node
|
|
9
|
+
// runtimes telemetry no-ops.
|
|
10
|
+
const NOOP_STORAGE = {
|
|
11
|
+
getStore: () => undefined,
|
|
12
|
+
run: (_store, fn) => fn(),
|
|
13
|
+
enterWith: () => { },
|
|
14
|
+
};
|
|
15
|
+
function getStorage() {
|
|
16
|
+
const existing = globalRef[STORAGE_KEY];
|
|
17
|
+
if (existing)
|
|
18
|
+
return existing;
|
|
19
|
+
const Ctor = getAsyncLocalStorageCtor();
|
|
20
|
+
const created = Ctor
|
|
21
|
+
? new Ctor()
|
|
22
|
+
: NOOP_STORAGE;
|
|
23
|
+
globalRef[STORAGE_KEY] = created;
|
|
24
|
+
return created;
|
|
25
|
+
}
|
|
8
26
|
export function createDatabaseRequestTelemetry() {
|
|
9
27
|
return {
|
|
10
28
|
operationCount: 0,
|
|
@@ -28,7 +46,7 @@ const startupState = globalRef[STARTUP_STATE_KEY] ??
|
|
|
28
46
|
telemetry: createDatabaseRequestTelemetry(),
|
|
29
47
|
});
|
|
30
48
|
function currentDatabaseTelemetry() {
|
|
31
|
-
const requestTelemetry =
|
|
49
|
+
const requestTelemetry = getStorage().getStore();
|
|
32
50
|
if (requestTelemetry)
|
|
33
51
|
return requestTelemetry;
|
|
34
52
|
if (!startupState.claimed && Date.now() <= startupState.captureUntil) {
|
|
@@ -43,10 +61,10 @@ export function claimStartupDatabaseTelemetry() {
|
|
|
43
61
|
return startupState.telemetry;
|
|
44
62
|
}
|
|
45
63
|
export function runWithDatabaseRequestTelemetry(telemetry, fn) {
|
|
46
|
-
return
|
|
64
|
+
return getStorage().run(telemetry, fn);
|
|
47
65
|
}
|
|
48
66
|
export function enterDatabaseRequestTelemetry(telemetry) {
|
|
49
|
-
|
|
67
|
+
getStorage().enterWith(telemetry);
|
|
50
68
|
}
|
|
51
69
|
export function beginDatabaseOperation(operation) {
|
|
52
70
|
const telemetry = currentDatabaseTelemetry();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-telemetry.js","sourceRoot":"","sources":["../../src/db/request-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"request-telemetry.js","sourceRoot":"","sources":["../../src/db/request-telemetry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAsyncLocalStorageCtor } from \"../shared/optional-node-builtins.js\";\n\nexport type DatabaseOperationKind = \"connect\" | \"query\";\n\nexport interface DatabaseRequestTelemetry {\n operationCount: number;\n queryCount: number;\n connectCount: number;\n retryCount: number;\n errorCount: number;\n timeoutCount: number;\n operationTotalMs: number;\n operationWallMs: number;\n queryTotalMs: number;\n connectTotalMs: number;\n slowestOperationMs: number;\n activeOperationCount: number;\n activeWindowStartedAt?: number;\n}\n\nconst STORAGE_KEY = Symbol.for(\n \"@agent-native/core/db.request-telemetry-storage\",\n);\nconst STARTUP_STATE_KEY = Symbol.for(\n \"@agent-native/core/db.startup-telemetry-state\",\n);\nconst STARTUP_CAPTURE_WINDOW_MS = 120_000;\ninterface StartupDatabaseTelemetryState {\n captureUntil: number;\n claimed: boolean;\n telemetry: DatabaseRequestTelemetry;\n}\ninterface TelemetryStorage {\n getStore(): DatabaseRequestTelemetry | undefined;\n run<T>(store: DatabaseRequestTelemetry, fn: () => T): T;\n enterWith(store: DatabaseRequestTelemetry): void;\n}\n\ntype GlobalWithDatabaseTelemetry = typeof globalThis & {\n [STORAGE_KEY]?: TelemetryStorage;\n [STARTUP_STATE_KEY]?: StartupDatabaseTelemetryState;\n};\n\nconst globalRef = globalThis as GlobalWithDatabaseTelemetry;\n\n// AsyncLocalStorage is resolved lazily, never at module load: this module can\n// land in the browser dev graph, where a top-level `new AsyncLocalStorage()`\n// would throw against Vite's externalized `node:async_hooks` stub. On non-Node\n// runtimes telemetry no-ops.\nconst NOOP_STORAGE: TelemetryStorage = {\n getStore: () => undefined,\n run: (_store, fn) => fn(),\n enterWith: () => {},\n};\n\nfunction getStorage(): TelemetryStorage {\n const existing = globalRef[STORAGE_KEY];\n if (existing) return existing;\n const Ctor = getAsyncLocalStorageCtor();\n const created: TelemetryStorage = Ctor\n ? new Ctor<DatabaseRequestTelemetry>()\n : NOOP_STORAGE;\n globalRef[STORAGE_KEY] = created;\n return created;\n}\n\nexport function createDatabaseRequestTelemetry(): DatabaseRequestTelemetry {\n return {\n operationCount: 0,\n queryCount: 0,\n connectCount: 0,\n retryCount: 0,\n errorCount: 0,\n timeoutCount: 0,\n operationTotalMs: 0,\n operationWallMs: 0,\n queryTotalMs: 0,\n connectTotalMs: 0,\n slowestOperationMs: 0,\n activeOperationCount: 0,\n };\n}\n\nconst startupState =\n globalRef[STARTUP_STATE_KEY] ??\n (globalRef[STARTUP_STATE_KEY] = {\n captureUntil: Date.now() + STARTUP_CAPTURE_WINDOW_MS,\n claimed: false,\n telemetry: createDatabaseRequestTelemetry(),\n });\n\nfunction currentDatabaseTelemetry(): DatabaseRequestTelemetry | undefined {\n const requestTelemetry = getStorage().getStore();\n if (requestTelemetry) return requestTelemetry;\n if (!startupState.claimed && Date.now() <= startupState.captureUntil) {\n return startupState.telemetry;\n }\n return undefined;\n}\n\nexport function claimStartupDatabaseTelemetry():\n | DatabaseRequestTelemetry\n | undefined {\n if (startupState.claimed) return undefined;\n startupState.claimed = true;\n return startupState.telemetry;\n}\n\nexport function runWithDatabaseRequestTelemetry<T>(\n telemetry: DatabaseRequestTelemetry,\n fn: () => T,\n): T {\n return getStorage().run(telemetry, fn);\n}\n\nexport function enterDatabaseRequestTelemetry(\n telemetry: DatabaseRequestTelemetry,\n): void {\n getStorage().enterWith(telemetry);\n}\n\nexport function beginDatabaseOperation(\n operation: DatabaseOperationKind,\n): (outcome: \"success\" | \"error\" | \"timeout\") => void {\n const telemetry = currentDatabaseTelemetry();\n if (!telemetry) return () => {};\n\n const startedAt = Date.now();\n if (telemetry.activeOperationCount === 0) {\n telemetry.activeWindowStartedAt = startedAt;\n }\n telemetry.activeOperationCount += 1;\n let completed = false;\n\n return (outcome) => {\n if (completed) return;\n completed = true;\n const completedAt = Date.now();\n const duration = Math.max(0, completedAt - startedAt);\n telemetry.operationCount += 1;\n telemetry.operationTotalMs += duration;\n telemetry.slowestOperationMs = Math.max(\n telemetry.slowestOperationMs,\n duration,\n );\n\n if (operation === \"connect\") {\n telemetry.connectCount += 1;\n telemetry.connectTotalMs += duration;\n } else {\n telemetry.queryCount += 1;\n telemetry.queryTotalMs += duration;\n }\n\n if (outcome !== \"success\") telemetry.errorCount += 1;\n if (outcome === \"timeout\") telemetry.timeoutCount += 1;\n\n telemetry.activeOperationCount = Math.max(\n 0,\n telemetry.activeOperationCount - 1,\n );\n if (\n telemetry.activeOperationCount === 0 &&\n telemetry.activeWindowStartedAt !== undefined\n ) {\n telemetry.operationWallMs += Math.max(\n 0,\n completedAt - telemetry.activeWindowStartedAt,\n );\n telemetry.activeWindowStartedAt = undefined;\n }\n };\n}\n\nexport function recordDatabaseRetry(): void {\n const telemetry = currentDatabaseTelemetry();\n if (telemetry) telemetry.retryCount += 1;\n}\n"]}
|
|
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
46
|
id?: undefined;
|
|
49
|
-
} | {
|
|
50
47
|
error?: undefined;
|
|
51
48
|
ok?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
|
|
53
|
+
error?: undefined;
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
68
68
|
//# sourceMappingURL=routes.d.ts.map
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — re-run the validator against the
|
|
54
54
|
* current stored value without changing anything. Useful for the "Test" button.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,12 +95,12 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
|
106
106
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -27,11 +27,11 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
37
37
|
//# sourceMappingURL=agent-engine-api-key-route.d.ts.map
|
package/dist/settings/store.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
1
|
+
import type { EventEmitter } from "node:events";
|
|
2
2
|
export declare function getSettingsEmitter(): EventEmitter;
|
|
3
3
|
export declare function getSetting(key: string): Promise<Record<string, unknown> | null>;
|
|
4
4
|
export interface StoreWriteOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA0ChD,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAsED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAgBzC;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CACP,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC/D,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAwClC;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CACxC,CAUA"}
|
package/dist/settings/store.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
1
|
import { getDbExec, isPostgres, intType } from "../db/client.js";
|
|
3
2
|
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
4
3
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
5
4
|
import { getRequestContext } from "../server/request-context.js";
|
|
5
|
+
import { createEventEmitter } from "../shared/optional-node-builtins.js";
|
|
6
6
|
let _initPromise;
|
|
7
7
|
// Per-request memoization of settings reads, keyed on the active
|
|
8
8
|
// AsyncLocalStorage RequestContext (WeakMap → freed with the request). One
|
|
@@ -25,10 +25,18 @@ function requestSettingsCache() {
|
|
|
25
25
|
}
|
|
26
26
|
return cache;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Created lazily so this module can be evaluated in the browser dev graph
|
|
29
|
+
// without a top-level `new EventEmitter()` tripping Vite's externalized
|
|
30
|
+
// `node:events` stub. The emitter drives server-side SSE fan-out only.
|
|
31
|
+
let _emitter;
|
|
32
|
+
function settingsEmitter() {
|
|
33
|
+
if (!_emitter)
|
|
34
|
+
_emitter = createEventEmitter();
|
|
30
35
|
return _emitter;
|
|
31
36
|
}
|
|
37
|
+
export function getSettingsEmitter() {
|
|
38
|
+
return settingsEmitter();
|
|
39
|
+
}
|
|
32
40
|
function settingsTable() {
|
|
33
41
|
return isPostgres() ? "public.settings" : "settings";
|
|
34
42
|
}
|
|
@@ -144,7 +152,7 @@ export async function mutateSetting(key, updater, options) {
|
|
|
144
152
|
if (result.rowsAffected === 0)
|
|
145
153
|
continue;
|
|
146
154
|
requestSettingsCache()?.set(key, nextRaw);
|
|
147
|
-
|
|
155
|
+
settingsEmitter().emit("settings", {
|
|
148
156
|
source: "settings",
|
|
149
157
|
type: "change",
|
|
150
158
|
key,
|
|
@@ -165,7 +173,7 @@ export async function putSetting(key, value, options) {
|
|
|
165
173
|
args: [key, JSON.stringify(value), Date.now()],
|
|
166
174
|
});
|
|
167
175
|
requestSettingsCache()?.set(key, JSON.stringify(value));
|
|
168
|
-
|
|
176
|
+
settingsEmitter().emit("settings", {
|
|
169
177
|
source: "settings",
|
|
170
178
|
type: "change",
|
|
171
179
|
key,
|
|
@@ -182,7 +190,7 @@ export async function deleteSetting(key, options) {
|
|
|
182
190
|
});
|
|
183
191
|
requestSettingsCache()?.set(key, null);
|
|
184
192
|
if (result.rowsAffected > 0) {
|
|
185
|
-
|
|
193
|
+
settingsEmitter().emit("settings", {
|
|
186
194
|
source: "settings",
|
|
187
195
|
type: "delete",
|
|
188
196
|
key,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from \"events\";\n\nimport { getDbExec, isPostgres, intType } from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport { getRequestContext } from \"../server/request-context.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Per-request memoization of settings reads, keyed on the active\n// AsyncLocalStorage RequestContext (WeakMap → freed with the request). One\n// action request can read the same setting several times (org resolution,\n// guards, the action body), and each read is a network round trip on\n// serverless Postgres. Mirrors the per-request session/org caches on\n// event.context. The cache holds the raw JSON string and re-parses per hit\n// so callers can't mutate a shared object; writes in the same request are\n// written through, while other in-flight requests keep their snapshot for\n// their own (short) lifetime.\nconst _requestSettingsCache = new WeakMap<object, Map<string, string | null>>();\n\nfunction requestSettingsCache(): Map<string, string | null> | null {\n const ctx = getRequestContext();\n if (!ctx || typeof ctx !== \"object\") return null;\n let cache = _requestSettingsCache.get(ctx);\n if (!cache) {\n cache = new Map();\n _requestSettingsCache.set(ctx, cache);\n }\n return cache;\n}\n\nconst _emitter = new EventEmitter();\n\nexport function getSettingsEmitter(): EventEmitter {\n return _emitter;\n}\n\nfunction settingsTable(): string {\n return isPostgres() ? \"public.settings\" : \"settings\";\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const table = settingsTable();\n const createSql = `\n CREATE TABLE IF NOT EXISTS ${table} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `settings` table and its poll index are virtually\n // always already present in production. Issuing `CREATE TABLE`/\n // `CREATE INDEX` still takes a lock that, in a fresh background-worker\n // process behind a concurrent connection on the shared Neon DB, can\n // block ~indefinitely (ACCESS EXCLUSIVE for CREATE TABLE; a write-\n // blocking SHARE lock for CREATE INDEX). `ensureTableExists` /\n // `ensureIndexExists` probe `information_schema`/`pg_indexes` first\n // (plain reads, no lock) and run DDL ONLY for what is actually missing,\n // bounding any DDL with a transaction-scoped `lock_timeout`. They also\n // re-probe after a swallowed lock-timeout and THROW if the schema is\n // still missing, so a timed-out DDL never poisons this init memo with\n // missing schema. `settingsTable()` is `public.settings` on Postgres;\n // the existence checks use the unqualified table name.\n await ensureTableExists(\"settings\", createSql);\n // Older deployments (pre BIGINT-compat) have a 32-bit `updated_at`; on\n // Postgres the `Date.now()` written on every setSetting overflows int4.\n // widenIntColumnsToBigInt already probes information_schema and only\n // ALTERs columns that are still int4 — a no-op on fresh/widened DBs.\n await widenIntColumnsToBigInt(\"settings\", [\"updated_at\"]);\n // Index for the poll watermark query: `SELECT MAX(updated_at)`.\n await ensureIndexExists(\n \"settings_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS settings_updated_at_idx ON ${table} (updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // No-op on SQLite (INTEGER is already 64-bit).\n await widenIntColumnsToBigInt(\"settings\", [\"updated_at\"]);\n // Index for the poll watermark query: `SELECT MAX(updated_at) FROM settings`.\n // MAX on an indexed column avoids a full-table scan on every poll cycle.\n // IF NOT EXISTS makes it idempotent on existing databases.\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS settings_updated_at_idx ON ${table} (updated_at)`,\n );\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function getSetting(\n key: string,\n): Promise<Record<string, unknown> | null> {\n const cache = requestSettingsCache();\n if (cache?.has(key)) {\n const cached = cache.get(key);\n return cached == null ? null : JSON.parse(cached);\n }\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute({\n sql: `SELECT value FROM ${table} WHERE key = ?`,\n args: [key],\n });\n const raw = rows.length === 0 ? null : (rows[0].value as string);\n cache?.set(key, raw);\n return raw == null ? null : JSON.parse(raw);\n}\n\nexport interface StoreWriteOptions {\n /** Tag identifying who initiated this write (e.g. a tab ID). */\n requestSource?: string;\n}\n\nconst SETTINGS_MUTATION_ATTEMPTS = 25;\n\n/**\n * Atomically derive and persist one setting with an optimistic raw-value CAS.\n * This works across SQLite/libSQL and Postgres and remains safe across\n * horizontally scaled processes where an in-memory mutex would not.\n * The updater may run more than once after contention and must not perform\n * external side effects.\n */\nexport async function mutateSetting(\n key: string,\n updater: (\n current: Record<string, unknown> | null,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>,\n options?: StoreWriteOptions,\n): Promise<Record<string, unknown>> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n for (let attempt = 0; attempt < SETTINGS_MUTATION_ATTEMPTS; attempt += 1) {\n // Deliberately bypass the request cache: a failed CAS means another\n // request committed a newer value and the next attempt must reread it.\n const snapshot = await client.execute({\n sql: `SELECT value FROM ${table} WHERE key = ?`,\n args: [key],\n });\n const raw =\n snapshot.rows.length === 0 ? null : (snapshot.rows[0]?.value as string);\n const current = raw == null ? null : JSON.parse(raw);\n const next = await updater(current);\n const nextRaw = JSON.stringify(next);\n const timestamp = Date.now();\n const result =\n raw == null\n ? await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO NOTHING`\n : `INSERT OR IGNORE INTO ${table} (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [key, nextRaw, timestamp],\n })\n : await client.execute({\n sql: `UPDATE ${table} SET value = ?, updated_at = ? WHERE key = ? AND value = ?`,\n args: [nextRaw, timestamp, key, raw],\n });\n if (result.rowsAffected === 0) continue;\n requestSettingsCache()?.set(key, nextRaw);\n _emitter.emit(\"settings\", {\n source: \"settings\",\n type: \"change\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n return JSON.parse(nextRaw);\n }\n throw new Error(`Setting ${key} changed too many times; retry the mutation.`);\n}\n\nexport async function putSetting(\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO ${table} (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [key, JSON.stringify(value), Date.now()],\n });\n requestSettingsCache()?.set(key, JSON.stringify(value));\n _emitter.emit(\"settings\", {\n source: \"settings\",\n type: \"change\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n}\n\nexport async function deleteSetting(\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const result = await client.execute({\n sql: `DELETE FROM ${table} WHERE key = ?`,\n args: [key],\n });\n requestSettingsCache()?.set(key, null);\n if (result.rowsAffected > 0) {\n _emitter.emit(\"settings\", {\n source: \"settings\",\n type: \"delete\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n return true;\n }\n return false;\n}\n\nexport async function getAllSettings(): Promise<\n Record<string, Record<string, unknown>>\n> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute(`SELECT key, value FROM ${table}`);\n const result: Record<string, Record<string, unknown>> = {};\n for (const row of rows) {\n result[row.key as string] = JSON.parse(row.value as string);\n }\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EventEmitter } from \"node:events\";\n\nimport { getDbExec, isPostgres, intType } from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport { getRequestContext } from \"../server/request-context.js\";\nimport { createEventEmitter } from \"../shared/optional-node-builtins.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Per-request memoization of settings reads, keyed on the active\n// AsyncLocalStorage RequestContext (WeakMap → freed with the request). One\n// action request can read the same setting several times (org resolution,\n// guards, the action body), and each read is a network round trip on\n// serverless Postgres. Mirrors the per-request session/org caches on\n// event.context. The cache holds the raw JSON string and re-parses per hit\n// so callers can't mutate a shared object; writes in the same request are\n// written through, while other in-flight requests keep their snapshot for\n// their own (short) lifetime.\nconst _requestSettingsCache = new WeakMap<object, Map<string, string | null>>();\n\nfunction requestSettingsCache(): Map<string, string | null> | null {\n const ctx = getRequestContext();\n if (!ctx || typeof ctx !== \"object\") return null;\n let cache = _requestSettingsCache.get(ctx);\n if (!cache) {\n cache = new Map();\n _requestSettingsCache.set(ctx, cache);\n }\n return cache;\n}\n\n// Created lazily so this module can be evaluated in the browser dev graph\n// without a top-level `new EventEmitter()` tripping Vite's externalized\n// `node:events` stub. The emitter drives server-side SSE fan-out only.\nlet _emitter: EventEmitter | undefined;\n\nfunction settingsEmitter(): EventEmitter {\n if (!_emitter) _emitter = createEventEmitter();\n return _emitter;\n}\n\nexport function getSettingsEmitter(): EventEmitter {\n return settingsEmitter();\n}\n\nfunction settingsTable(): string {\n return isPostgres() ? \"public.settings\" : \"settings\";\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const table = settingsTable();\n const createSql = `\n CREATE TABLE IF NOT EXISTS ${table} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `settings` table and its poll index are virtually\n // always already present in production. Issuing `CREATE TABLE`/\n // `CREATE INDEX` still takes a lock that, in a fresh background-worker\n // process behind a concurrent connection on the shared Neon DB, can\n // block ~indefinitely (ACCESS EXCLUSIVE for CREATE TABLE; a write-\n // blocking SHARE lock for CREATE INDEX). `ensureTableExists` /\n // `ensureIndexExists` probe `information_schema`/`pg_indexes` first\n // (plain reads, no lock) and run DDL ONLY for what is actually missing,\n // bounding any DDL with a transaction-scoped `lock_timeout`. They also\n // re-probe after a swallowed lock-timeout and THROW if the schema is\n // still missing, so a timed-out DDL never poisons this init memo with\n // missing schema. `settingsTable()` is `public.settings` on Postgres;\n // the existence checks use the unqualified table name.\n await ensureTableExists(\"settings\", createSql);\n // Older deployments (pre BIGINT-compat) have a 32-bit `updated_at`; on\n // Postgres the `Date.now()` written on every setSetting overflows int4.\n // widenIntColumnsToBigInt already probes information_schema and only\n // ALTERs columns that are still int4 — a no-op on fresh/widened DBs.\n await widenIntColumnsToBigInt(\"settings\", [\"updated_at\"]);\n // Index for the poll watermark query: `SELECT MAX(updated_at)`.\n await ensureIndexExists(\n \"settings_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS settings_updated_at_idx ON ${table} (updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // No-op on SQLite (INTEGER is already 64-bit).\n await widenIntColumnsToBigInt(\"settings\", [\"updated_at\"]);\n // Index for the poll watermark query: `SELECT MAX(updated_at) FROM settings`.\n // MAX on an indexed column avoids a full-table scan on every poll cycle.\n // IF NOT EXISTS makes it idempotent on existing databases.\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS settings_updated_at_idx ON ${table} (updated_at)`,\n );\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function getSetting(\n key: string,\n): Promise<Record<string, unknown> | null> {\n const cache = requestSettingsCache();\n if (cache?.has(key)) {\n const cached = cache.get(key);\n return cached == null ? null : JSON.parse(cached);\n }\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute({\n sql: `SELECT value FROM ${table} WHERE key = ?`,\n args: [key],\n });\n const raw = rows.length === 0 ? null : (rows[0].value as string);\n cache?.set(key, raw);\n return raw == null ? null : JSON.parse(raw);\n}\n\nexport interface StoreWriteOptions {\n /** Tag identifying who initiated this write (e.g. a tab ID). */\n requestSource?: string;\n}\n\nconst SETTINGS_MUTATION_ATTEMPTS = 25;\n\n/**\n * Atomically derive and persist one setting with an optimistic raw-value CAS.\n * This works across SQLite/libSQL and Postgres and remains safe across\n * horizontally scaled processes where an in-memory mutex would not.\n * The updater may run more than once after contention and must not perform\n * external side effects.\n */\nexport async function mutateSetting(\n key: string,\n updater: (\n current: Record<string, unknown> | null,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>,\n options?: StoreWriteOptions,\n): Promise<Record<string, unknown>> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n for (let attempt = 0; attempt < SETTINGS_MUTATION_ATTEMPTS; attempt += 1) {\n // Deliberately bypass the request cache: a failed CAS means another\n // request committed a newer value and the next attempt must reread it.\n const snapshot = await client.execute({\n sql: `SELECT value FROM ${table} WHERE key = ?`,\n args: [key],\n });\n const raw =\n snapshot.rows.length === 0 ? null : (snapshot.rows[0]?.value as string);\n const current = raw == null ? null : JSON.parse(raw);\n const next = await updater(current);\n const nextRaw = JSON.stringify(next);\n const timestamp = Date.now();\n const result =\n raw == null\n ? await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO NOTHING`\n : `INSERT OR IGNORE INTO ${table} (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [key, nextRaw, timestamp],\n })\n : await client.execute({\n sql: `UPDATE ${table} SET value = ?, updated_at = ? WHERE key = ? AND value = ?`,\n args: [nextRaw, timestamp, key, raw],\n });\n if (result.rowsAffected === 0) continue;\n requestSettingsCache()?.set(key, nextRaw);\n settingsEmitter().emit(\"settings\", {\n source: \"settings\",\n type: \"change\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n return JSON.parse(nextRaw);\n }\n throw new Error(`Setting ${key} changed too many times; retry the mutation.`);\n}\n\nexport async function putSetting(\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO ${table} (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [key, JSON.stringify(value), Date.now()],\n });\n requestSettingsCache()?.set(key, JSON.stringify(value));\n settingsEmitter().emit(\"settings\", {\n source: \"settings\",\n type: \"change\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n}\n\nexport async function deleteSetting(\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const result = await client.execute({\n sql: `DELETE FROM ${table} WHERE key = ?`,\n args: [key],\n });\n requestSettingsCache()?.set(key, null);\n if (result.rowsAffected > 0) {\n settingsEmitter().emit(\"settings\", {\n source: \"settings\",\n type: \"delete\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n return true;\n }\n return false;\n}\n\nexport async function getAllSettings(): Promise<\n Record<string, Record<string, unknown>>\n> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute(`SELECT key, value FROM ${table}`);\n const result: Record<string, Record<string, unknown>> = {};\n for (const row of rows) {\n result[row.key as string] = JSON.parse(row.value as string);\n }\n return result;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
export type AsyncLocalStorageCtor = new <T>() => AsyncLocalStorage<T>;
|
|
16
|
+
export declare function getAsyncLocalStorageCtor(): AsyncLocalStorageCtor | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a real Node EventEmitter on Node, or a no-op emitter elsewhere. The
|
|
19
|
+
* emitters that use this drive server-side SSE fan-out; nothing subscribes in
|
|
20
|
+
* the browser, so the no-op fallback is inert rather than wrong.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createEventEmitter(): EventEmitter;
|
|
23
|
+
//# sourceMappingURL=optional-node-builtins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optional-node-builtins.d.ts","sourceRoot":"","sources":["../../src/shared/optional-node-builtins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqBhD,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAEtE,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,SAAS,CAI5E;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAgBjD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function nodeBuiltin(name) {
|
|
2
|
+
const proc = typeof process === "undefined" ? undefined : process;
|
|
3
|
+
if (typeof window !== "undefined" ||
|
|
4
|
+
!proc ||
|
|
5
|
+
!proc.versions?.node ||
|
|
6
|
+
typeof proc.getBuiltinModule !== "function") {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
return proc.getBuiltinModule(name);
|
|
10
|
+
}
|
|
11
|
+
export function getAsyncLocalStorageCtor() {
|
|
12
|
+
return nodeBuiltin("node:async_hooks")?.AsyncLocalStorage;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a real Node EventEmitter on Node, or a no-op emitter elsewhere. The
|
|
16
|
+
* emitters that use this drive server-side SSE fan-out; nothing subscribes in
|
|
17
|
+
* the browser, so the no-op fallback is inert rather than wrong.
|
|
18
|
+
*/
|
|
19
|
+
export function createEventEmitter() {
|
|
20
|
+
const Ctor = nodeBuiltin("node:events")?.EventEmitter;
|
|
21
|
+
if (Ctor)
|
|
22
|
+
return new Ctor();
|
|
23
|
+
const noop = {
|
|
24
|
+
on: () => noop,
|
|
25
|
+
off: () => noop,
|
|
26
|
+
once: () => noop,
|
|
27
|
+
addListener: () => noop,
|
|
28
|
+
removeListener: () => noop,
|
|
29
|
+
removeAllListeners: () => noop,
|
|
30
|
+
setMaxListeners: () => noop,
|
|
31
|
+
emit: () => false,
|
|
32
|
+
};
|
|
33
|
+
return noop;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=optional-node-builtins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optional-node-builtins.js","sourceRoot":"","sources":["../../src/shared/optional-node-builtins.ts"],"names":[],"mappings":"AAoBA,SAAS,WAAW,CAAI,IAAY;IAClC,MAAM,IAAI,GACR,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,OAA0B,CAAC;IAC3E,IACE,OAAO,MAAM,KAAK,WAAW;QAC7B,CAAC,IAAI;QACL,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI;QACpB,OAAO,IAAI,CAAC,gBAAgB,KAAK,UAAU,EAC3C,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAkB,CAAC;AACtD,CAAC;AAID,MAAM,UAAU,wBAAwB;IACtC,OAAO,WAAW,CAChB,kBAAkB,CACnB,EAAE,iBAAiB,CAAC;AACvB,CAAC;AAID;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAG,WAAW,CACtB,aAAa,CACd,EAAE,YAAY,CAAC;IAChB,IAAI,IAAI;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG;QACX,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI;QACd,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI;QACf,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI;QAChB,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;QAC1B,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI;QAC9B,eAAe,EAAE,GAAG,EAAE,CAAC,IAAI;QAC3B,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK;KAClB,CAAC;IACF,OAAO,IAA+B,CAAC;AACzC,CAAC","sourcesContent":["/**\n * Browser-safe accessors for optional Node builtins.\n *\n * `node:async_hooks` and `node:events` are server-only, but the modules that\n * use them (db telemetry, settings/resource emitters) can land in the browser\n * dev graph before tree-shaking runs. A static `import { X } from \"node:...\"`\n * resolves to Vite's externalized stub and throws the instant the value is\n * touched at module load. We read the builtin through `process.getBuiltinModule`\n * instead — the same guard `server/request-context.ts` uses — so there is no\n * static `node:` import to evaluate in the browser, and callers fall back to a\n * no-op on non-Node runtimes (browser, or Node without getBuiltinModule).\n */\nimport type { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { EventEmitter } from \"node:events\";\n\ntype BuiltinProcess = {\n versions?: { node?: string };\n getBuiltinModule?: (name: string) => unknown;\n};\n\nfunction nodeBuiltin<T>(name: string): T | undefined {\n const proc =\n typeof process === \"undefined\" ? undefined : (process as BuiltinProcess);\n if (\n typeof window !== \"undefined\" ||\n !proc ||\n !proc.versions?.node ||\n typeof proc.getBuiltinModule !== \"function\"\n ) {\n return undefined;\n }\n return proc.getBuiltinModule(name) as T | undefined;\n}\n\nexport type AsyncLocalStorageCtor = new <T>() => AsyncLocalStorage<T>;\n\nexport function getAsyncLocalStorageCtor(): AsyncLocalStorageCtor | undefined {\n return nodeBuiltin<{ AsyncLocalStorage: AsyncLocalStorageCtor }>(\n \"node:async_hooks\",\n )?.AsyncLocalStorage;\n}\n\ntype EventEmitterCtor = new () => EventEmitter;\n\n/**\n * Returns a real Node EventEmitter on Node, or a no-op emitter elsewhere. The\n * emitters that use this drive server-side SSE fan-out; nothing subscribes in\n * the browser, so the no-op fallback is inert rather than wrong.\n */\nexport function createEventEmitter(): EventEmitter {\n const Ctor = nodeBuiltin<{ EventEmitter: EventEmitterCtor }>(\n \"node:events\",\n )?.EventEmitter;\n if (Ctor) return new Ctor();\n const noop = {\n on: () => noop,\n off: () => noop,\n once: () => noop,\n addListener: () => noop,\n removeListener: () => noop,\n removeAllListeners: () => noop,\n setMaxListeners: () => noop,\n emit: () => false,\n };\n return noop as unknown as EventEmitter;\n}\n"]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-native-toolkit
|
|
3
|
+
description: >-
|
|
4
|
+
Inventory and ownership rules for shared Agent Native workspace UI. Use
|
|
5
|
+
before building app chrome, settings, navigation, sharing, collaboration,
|
|
6
|
+
setup, history, comments, chat rails, agent UX, or repeated workspace behavior.
|
|
7
|
+
scope: dev
|
|
8
|
+
metadata:
|
|
9
|
+
internal: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Agent-Native Toolkit
|
|
13
|
+
|
|
14
|
+
Use this skill when deciding whether app chrome, settings, collaboration,
|
|
15
|
+
sharing, navigation, organization, setup, history, comments, or agent UX should
|
|
16
|
+
be built app-locally or moved into reusable framework/toolkit pieces.
|
|
17
|
+
|
|
18
|
+
## Core Rule
|
|
19
|
+
|
|
20
|
+
Apps own domain models, domain actions, and product-specific workflows. The
|
|
21
|
+
framework and `@agent-native/toolkit` own repeated workspace behavior users
|
|
22
|
+
expect to work the same everywhere.
|
|
23
|
+
|
|
24
|
+
Move behavior into shared toolkit primitives when it is:
|
|
25
|
+
|
|
26
|
+
- workspace-wide, such as settings, nav, search, org membership, or setup
|
|
27
|
+
- agent-visible, such as context, actions, run progress, or proof-of-done
|
|
28
|
+
- governed, such as secrets, permissions, sharing, audit, or billing
|
|
29
|
+
- repeated by two or more apps
|
|
30
|
+
- not tied to one domain model
|
|
31
|
+
|
|
32
|
+
Keep behavior app-local when the abstraction would hide important domain
|
|
33
|
+
language or make a simple app-specific workflow harder to understand.
|
|
34
|
+
|
|
35
|
+
## Discover Before Building
|
|
36
|
+
|
|
37
|
+
Before creating an app-local version of repeated workspace or agent UI:
|
|
38
|
+
|
|
39
|
+
1. Check the reusable kits below and the installed package documentation.
|
|
40
|
+
2. Search installed public components and source with `docs-search` and
|
|
41
|
+
`source-search`.
|
|
42
|
+
3. Run `agent-native eject --list` to see the version-matched units published
|
|
43
|
+
by the packages installed in this app.
|
|
44
|
+
4. Read `customizing-agent-native` and configure, compose, or eject the
|
|
45
|
+
smallest unit instead of recreating shared behavior from memory.
|
|
46
|
+
|
|
47
|
+
Use public package exports at runtime. Published source and ejection manifests
|
|
48
|
+
are discovery and ownership-transfer mechanisms, not private runtime APIs.
|
|
49
|
+
|
|
50
|
+
## Settings Direction
|
|
51
|
+
|
|
52
|
+
Durable settings belong in the Settings app or a registered settings route. The
|
|
53
|
+
agent sidebar should not become a second settings app. It can show contextual
|
|
54
|
+
quick controls and deep links such as:
|
|
55
|
+
|
|
56
|
+
- `/settings/ai`
|
|
57
|
+
- `/settings/connections`
|
|
58
|
+
- `/settings/secrets`
|
|
59
|
+
- `/settings/usage`
|
|
60
|
+
- `/settings/apps/:appId`
|
|
61
|
+
|
|
62
|
+
When adding a new API key, OAuth grant, provider connection, model selector, app
|
|
63
|
+
preference, notification preference, or usage/billing surface, register it as a
|
|
64
|
+
settings tab or app settings panel first. Only add sidebar UI when it is needed
|
|
65
|
+
in the moment of agent use.
|
|
66
|
+
|
|
67
|
+
## Reusable Kits
|
|
68
|
+
|
|
69
|
+
- **Settings kit**: a searchable settings page with account, workspace, AI
|
|
70
|
+
models, LLM keys, connections, secrets, usage, notifications, changelog, and
|
|
71
|
+
app-specific panels. Search is on by default; register a `SettingsSearchEntry`
|
|
72
|
+
per control so users find settings by name across tabs.
|
|
73
|
+
- **Collaboration kit**: Yjs docs, presence, agent presence, live cursors,
|
|
74
|
+
remote selections, recent edit highlights, real-time sync indicators, and
|
|
75
|
+
undo/redo grouping.
|
|
76
|
+
- **Sharing kit**: private/workspace/org/public-link access, invites, roles,
|
|
77
|
+
expirations, agent-readable links, and resource registration.
|
|
78
|
+
- **Navigation and command kit**: app shell, side nav, breadcrumbs, app switcher,
|
|
79
|
+
command palette entries, recent resources, pinned resources, and global search.
|
|
80
|
+
- **Organization kit**: folders, tags, favorites, archive, trash, ownership,
|
|
81
|
+
membership, and common resource metadata.
|
|
82
|
+
- **Setup and connections kit**: declarative setup requirements, model readiness,
|
|
83
|
+
missing-secret states, OAuth grants, and provider connection health.
|
|
84
|
+
- **Agent UX kit**: sidebar, composer, staged context, mentions, voice, human
|
|
85
|
+
approval, generative UI, progress, and screen-state exposure.
|
|
86
|
+
- **Chat history kit**: presentational chat lists and recent-chat rails belong
|
|
87
|
+
in Toolkit; Core keeps thread persistence, agent execution, transport, and
|
|
88
|
+
page-to-sidebar handoff. Apps inject routing, labels, and domain actions.
|
|
89
|
+
- **Agent page kit**: the full-page `/agent` surface (`AgentTabsPage` from
|
|
90
|
+
`@agent-native/core/client`) with Context, Files, Connections, Jobs, and
|
|
91
|
+
Access tabs plus a Personal/Organization scope toggle. The canonical home
|
|
92
|
+
for context transparency, MCP servers, A2A remote agents, recurring
|
|
93
|
+
jobs/automations, and external-client connect flows. See the `agent-page`
|
|
94
|
+
skill.
|
|
95
|
+
- **History and recovery kit**: audit log, activity feed, version history,
|
|
96
|
+
checkpoints, undo, redo, restore, and proof-of-done.
|
|
97
|
+
- **Comments and review kit**: anchored comments, pins, mentions, review
|
|
98
|
+
requests, resolved threads, agent follow-up tasks, and notifications.
|
|
99
|
+
- **Workflow and observability kit**: notifications, approvals, scheduled work,
|
|
100
|
+
background runs, recurring jobs, traces, evals, feedback, and run timelines.
|
|
101
|
+
|
|
102
|
+
## Implementation Checklist
|
|
103
|
+
|
|
104
|
+
When adding or refactoring one of these areas:
|
|
105
|
+
|
|
106
|
+
1. Search existing framework and template code for duplicated UI or actions.
|
|
107
|
+
2. Decide the shared contract: data shape, action API, React component/hook, and
|
|
108
|
+
app adapter points.
|
|
109
|
+
3. Keep shared data provider-agnostic and scoped by auth/sharing rules.
|
|
110
|
+
4. Expose the same capability to the UI and agent through actions or documented
|
|
111
|
+
client helpers.
|
|
112
|
+
5. Register app-specific labels, routes, resource adapters, and settings panels
|
|
113
|
+
instead of hardcoding app names in core UI.
|
|
114
|
+
6. Update docs and relevant skills so future apps discover the shared path.
|
|
115
|
+
7. Keep the component easy to adopt piecemeal: expose props/slots first and
|
|
116
|
+
ship readable source plus a complete ejection unit so apps can take ownership
|
|
117
|
+
of the smallest feature when needed. See `customizing-agent-native` for the
|
|
118
|
+
configure → compose → eject → propose seam ladder.
|
|
119
|
+
|
|
120
|
+
## Related Skills
|
|
121
|
+
|
|
122
|
+
Read these alongside this skill when the work touches the specific area:
|
|
123
|
+
|
|
124
|
+
- `sharing`
|
|
125
|
+
- `real-time-collab`
|
|
126
|
+
- `real-time-sync`
|
|
127
|
+
- `client-side-routing`
|
|
128
|
+
- `context-awareness`
|
|
129
|
+
- `onboarding`
|
|
130
|
+
- `secrets`
|
|
131
|
+
- `audit-log`
|
|
132
|
+
- `observability`
|
|
133
|
+
- `frontend-design`
|
|
@@ -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",
|
|
@@ -53,6 +53,8 @@ workflow needs durable UI around the conversation.
|
|
|
53
53
|
first-party template implementation examples.
|
|
54
54
|
- Prefer those installed docs over memory or public docs when package APIs,
|
|
55
55
|
generated-app conventions, workspaces, actions, or agent surfaces are involved.
|
|
56
|
+
- Before building common workspace or agent UI, read `agent-native-toolkit` to
|
|
57
|
+
inventory existing public kits and installed package seams.
|
|
56
58
|
- Read `customizing-agent-native` before overriding the chat shell or shared UI.
|
|
57
59
|
Keep Core thread/runtime behavior and use the supported ladder: configure →
|
|
58
60
|
compose → eject the smallest presentation unit → propose a shared seam.
|
|
@@ -61,6 +63,7 @@ workflow needs durable UI around the conversation.
|
|
|
61
63
|
## Skills
|
|
62
64
|
|
|
63
65
|
Read the relevant root skill before implementation: `adding-a-feature`,
|
|
64
|
-
`actions`, `agent-native-docs`, `
|
|
66
|
+
`actions`, `agent-native-docs`, `agent-native-toolkit`,
|
|
67
|
+
`customizing-agent-native`, `storing-data`,
|
|
65
68
|
`real-time-sync`, `security`, `delegate-to-agent`, `frontend-design`, `shadcn-ui`, and
|
|
66
69
|
`self-modifying-code`.
|