@agent-native/core 0.84.21 → 0.84.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +8 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +27 -4
- package/corpus/core/src/client/guided-questions.tsx +11 -6
- package/corpus/core/src/client/tool-display.ts +8 -0
- package/corpus/core/src/file-upload/builder.ts +7 -2
- package/corpus/core/src/notifications/channels.ts +235 -38
- package/corpus/core/src/server/analytics.ts +32 -6
- package/corpus/core/src/server/ssr-handler.ts +31 -12
- package/corpus/templates/analytics/AGENTS.md +21 -0
- package/corpus/templates/analytics/README.md +35 -0
- package/corpus/templates/analytics/actions/delete-analytics-alert-rule.ts +23 -0
- package/corpus/templates/analytics/actions/list-analytics-alert-rules.ts +20 -0
- package/corpus/templates/analytics/actions/run-analytics-alerts.ts +23 -0
- package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +69 -0
- package/corpus/templates/analytics/changelog/2026-07-01-analytics-can-alert-when-first-party-events-spike.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-01-session-replays-play-back-in-production-instead-of-showing.md +6 -0
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
- package/corpus/templates/analytics/server/db/schema.ts +55 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +5 -2
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +71 -0
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +734 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +10 -3
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +54 -0
- package/corpus/templates/analytics/server/plugins/db.ts +100 -0
- package/corpus/templates/analytics/server/routes/api/analytics-alerts/run.post.ts +60 -0
- package/corpus/templates/assets/README.md +30 -8
- package/corpus/templates/brain/README.md +20 -552
- package/corpus/templates/calendar/README.md +22 -28
- package/corpus/templates/chat/README.md +32 -0
- package/corpus/templates/clips/README.md +37 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +18 -7
- package/corpus/templates/clips/app/components/library/recording-card.tsx +42 -5
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +41 -1
- package/corpus/templates/clips/changelog/2026-07-01-move-to-folder-now-opens-folder-choices-directly-instead-of.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +37 -0
- package/corpus/templates/content/AGENTS.md +7 -3
- package/corpus/templates/content/README.md +22 -34
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +4 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +58 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +257 -24
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +41 -15
- package/corpus/templates/content/app/i18n/zh-TW.ts +25 -7
- package/corpus/templates/content/app/i18n-data.ts +278 -40
- package/corpus/templates/content/changelog/2026-06-30-database-once-again-creates-an-inline-database-in-the-curren.md +6 -0
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/design/AGENTS.md +7 -6
- package/corpus/templates/design/README.md +33 -0
- package/corpus/templates/design/actions/delete-file.ts +86 -10
- package/corpus/templates/design/actions/get-design-snapshot.ts +13 -0
- package/corpus/templates/design/actions/present-design-variants.ts +11 -13
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2 -2
- package/corpus/templates/dispatch/README.md +34 -0
- package/corpus/templates/forms/README.md +34 -0
- package/corpus/templates/macros/README.md +28 -0
- package/corpus/templates/mail/README.md +35 -0
- package/corpus/templates/plan/README.md +30 -118
- package/corpus/templates/slides/README.md +34 -0
- package/corpus/templates/videos/README.md +22 -201
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +28 -4
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +10 -6
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/tool-display.d.ts.map +1 -1
- package/dist/client/tool-display.js +8 -0
- package/dist/client/tool-display.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +6 -2
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/notifications/channels.d.ts +10 -0
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +172 -20
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/analytics.d.ts +16 -0
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +30 -7
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +28 -12
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1591,7 +1591,8 @@ export async function readSessionReplayChunkBytes(
|
|
|
1591
1591
|
recording: SessionRecordingSummary;
|
|
1592
1592
|
seq: number;
|
|
1593
1593
|
checksum: string;
|
|
1594
|
-
|
|
1594
|
+
/** Decompressed replay-chunk JSON text (a serialized rrweb events array). */
|
|
1595
|
+
json: string;
|
|
1595
1596
|
}> {
|
|
1596
1597
|
const recording = await getSessionReplaySummary(recordingId, scope);
|
|
1597
1598
|
const db = getDb() as any;
|
|
@@ -1608,6 +1609,12 @@ export async function readSessionReplayChunkBytes(
|
|
|
1608
1609
|
.limit(1);
|
|
1609
1610
|
if (!row) throw replayError("Session replay chunk not found", 404);
|
|
1610
1611
|
|
|
1612
|
+
// Return decompressed JSON and let normal Accept-Encoding negotiation handle
|
|
1613
|
+
// wire compression. We intentionally do NOT hand back a pre-gzipped body with
|
|
1614
|
+
// a manual `Content-Encoding: gzip` header: serverless hosts (Netlify) mangle
|
|
1615
|
+
// binary function bodies and re-negotiate compression, which corrupted replay
|
|
1616
|
+
// chunk downloads in production and left the player blank. Storing gzip at
|
|
1617
|
+
// rest is unchanged — we just gunzip before serving.
|
|
1611
1618
|
if (row.storageKind === "blob" && row.storageRef) {
|
|
1612
1619
|
const ref = decodeReplayBlobRef(row.storageRef);
|
|
1613
1620
|
if (!ref)
|
|
@@ -1617,7 +1624,7 @@ export async function readSessionReplayChunkBytes(
|
|
|
1617
1624
|
recording,
|
|
1618
1625
|
seq: row.seq,
|
|
1619
1626
|
checksum: row.checksum,
|
|
1620
|
-
|
|
1627
|
+
json: gunzipSync(Buffer.from(blob.data)).toString("utf8"),
|
|
1621
1628
|
};
|
|
1622
1629
|
}
|
|
1623
1630
|
if (!row.inlineData)
|
|
@@ -1626,7 +1633,7 @@ export async function readSessionReplayChunkBytes(
|
|
|
1626
1633
|
recording,
|
|
1627
1634
|
seq: row.seq,
|
|
1628
1635
|
checksum: row.checksum,
|
|
1629
|
-
|
|
1636
|
+
json: row.inlineData,
|
|
1630
1637
|
};
|
|
1631
1638
|
}
|
|
1632
1639
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { runAnalyticsAlertsOnce } from "../jobs/analytics-alerts";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_INTERVAL_MS = 60_000;
|
|
4
|
+
let skippingLogged = false;
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
var __AGENT_NATIVE_ANALYTICS_ALERT_SCHEDULED_RUNTIME__: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function platformSchedulerOwnsAlerts(): boolean {
|
|
11
|
+
return (
|
|
12
|
+
process.env.NETLIFY === "true" ||
|
|
13
|
+
globalThis.__AGENT_NATIVE_ANALYTICS_ALERT_SCHEDULED_RUNTIME__ === true
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function intervalMs(): number {
|
|
18
|
+
const raw = process.env.ANALYTICS_ALERT_INTERVAL_MS?.trim();
|
|
19
|
+
if (!raw) return DEFAULT_INTERVAL_MS;
|
|
20
|
+
const parsed = Number.parseInt(raw, 10);
|
|
21
|
+
return Number.isFinite(parsed) && parsed >= 10_000
|
|
22
|
+
? parsed
|
|
23
|
+
: DEFAULT_INTERVAL_MS;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function registerAnalyticsAlertJobs(): void {
|
|
27
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
28
|
+
const flag =
|
|
29
|
+
process.env.ANALYTICS_ALERT_JOBS ?? process.env.RUN_BACKGROUND_JOBS;
|
|
30
|
+
const enabled =
|
|
31
|
+
!platformSchedulerOwnsAlerts() &&
|
|
32
|
+
(flag === "1" || (isProd && flag !== "0"));
|
|
33
|
+
|
|
34
|
+
if (!enabled) {
|
|
35
|
+
if (!skippingLogged) {
|
|
36
|
+
console.log(
|
|
37
|
+
platformSchedulerOwnsAlerts()
|
|
38
|
+
? "[analytics-alerts] Skipping in-process cron because the platform scheduler owns alert sweeps."
|
|
39
|
+
: "[analytics-alerts] Skipping background cron (set ANALYTICS_ALERT_JOBS=1 or RUN_BACKGROUND_JOBS=1 to enable in dev; on by default in production)",
|
|
40
|
+
);
|
|
41
|
+
skippingLogged = true;
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ms = intervalMs();
|
|
47
|
+
setInterval(() => {
|
|
48
|
+
runAnalyticsAlertsOnce().catch((err) =>
|
|
49
|
+
console.error("[analytics-alerts] interval failed:", err),
|
|
50
|
+
);
|
|
51
|
+
}, ms);
|
|
52
|
+
|
|
53
|
+
console.log(`[analytics-alerts] Recurring alert sweep every ${ms / 1000}s.`);
|
|
54
|
+
}
|
|
@@ -513,6 +513,106 @@ export default runMigrations(
|
|
|
513
513
|
sqlite: `UPDATE session_replay_chunks SET started_at = CASE WHEN started_at IS NOT NULL AND substr(started_at, 1, 10) > date('now') THEN min(COALESCE(NULLIF(created_at, ''), datetime('now')), datetime('now')) ELSE started_at END, ended_at = CASE WHEN ended_at IS NOT NULL AND substr(ended_at, 1, 10) > date('now') THEN min(COALESCE(NULLIF(created_at, ''), datetime('now')), datetime('now')) ELSE ended_at END WHERE (started_at IS NOT NULL AND substr(started_at, 1, 10) > date('now')) OR (ended_at IS NOT NULL AND substr(ended_at, 1, 10) > date('now'))`,
|
|
514
514
|
},
|
|
515
515
|
},
|
|
516
|
+
{
|
|
517
|
+
version: 75,
|
|
518
|
+
sql: {
|
|
519
|
+
postgres: `CREATE TABLE IF NOT EXISTS analytics_alert_rules (
|
|
520
|
+
id TEXT PRIMARY KEY,
|
|
521
|
+
name TEXT NOT NULL,
|
|
522
|
+
description TEXT NOT NULL DEFAULT '',
|
|
523
|
+
event_name TEXT,
|
|
524
|
+
filters TEXT NOT NULL DEFAULT '[]',
|
|
525
|
+
threshold_mode TEXT NOT NULL DEFAULT 'event_count',
|
|
526
|
+
distinct_by TEXT,
|
|
527
|
+
threshold INTEGER NOT NULL DEFAULT 1,
|
|
528
|
+
window_minutes INTEGER NOT NULL DEFAULT 10,
|
|
529
|
+
cooldown_minutes INTEGER NOT NULL DEFAULT 30,
|
|
530
|
+
severity TEXT NOT NULL DEFAULT 'warning',
|
|
531
|
+
channels TEXT NOT NULL DEFAULT '["inbox"]',
|
|
532
|
+
email_recipients TEXT NOT NULL DEFAULT '[]',
|
|
533
|
+
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
534
|
+
last_evaluated_at TEXT,
|
|
535
|
+
last_triggered_at TEXT,
|
|
536
|
+
last_status TEXT,
|
|
537
|
+
last_error TEXT,
|
|
538
|
+
created_at TEXT NOT NULL DEFAULT (now()::text),
|
|
539
|
+
updated_at TEXT NOT NULL DEFAULT (now()::text),
|
|
540
|
+
owner_email TEXT NOT NULL DEFAULT 'local@localhost',
|
|
541
|
+
org_id TEXT
|
|
542
|
+
)`,
|
|
543
|
+
sqlite: `CREATE TABLE IF NOT EXISTS analytics_alert_rules (
|
|
544
|
+
id TEXT PRIMARY KEY,
|
|
545
|
+
name TEXT NOT NULL,
|
|
546
|
+
description TEXT NOT NULL DEFAULT '',
|
|
547
|
+
event_name TEXT,
|
|
548
|
+
filters TEXT NOT NULL DEFAULT '[]',
|
|
549
|
+
threshold_mode TEXT NOT NULL DEFAULT 'event_count',
|
|
550
|
+
distinct_by TEXT,
|
|
551
|
+
threshold INTEGER NOT NULL DEFAULT 1,
|
|
552
|
+
window_minutes INTEGER NOT NULL DEFAULT 10,
|
|
553
|
+
cooldown_minutes INTEGER NOT NULL DEFAULT 30,
|
|
554
|
+
severity TEXT NOT NULL DEFAULT 'warning',
|
|
555
|
+
channels TEXT NOT NULL DEFAULT '["inbox"]',
|
|
556
|
+
email_recipients TEXT NOT NULL DEFAULT '[]',
|
|
557
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
558
|
+
last_evaluated_at TEXT,
|
|
559
|
+
last_triggered_at TEXT,
|
|
560
|
+
last_status TEXT,
|
|
561
|
+
last_error TEXT,
|
|
562
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
563
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
564
|
+
owner_email TEXT NOT NULL DEFAULT 'local@localhost',
|
|
565
|
+
org_id TEXT
|
|
566
|
+
)`,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
version: 76,
|
|
571
|
+
sql: {
|
|
572
|
+
postgres: `CREATE TABLE IF NOT EXISTS analytics_alert_incidents (
|
|
573
|
+
id TEXT PRIMARY KEY,
|
|
574
|
+
rule_id TEXT NOT NULL,
|
|
575
|
+
triggered_at TEXT NOT NULL,
|
|
576
|
+
window_start TEXT NOT NULL,
|
|
577
|
+
window_end TEXT NOT NULL,
|
|
578
|
+
threshold INTEGER NOT NULL,
|
|
579
|
+
observed_value INTEGER NOT NULL,
|
|
580
|
+
event_count INTEGER NOT NULL,
|
|
581
|
+
severity TEXT NOT NULL,
|
|
582
|
+
channels TEXT NOT NULL DEFAULT '[]',
|
|
583
|
+
sample_events TEXT NOT NULL DEFAULT '[]',
|
|
584
|
+
notification_id TEXT,
|
|
585
|
+
created_at TEXT NOT NULL DEFAULT (now()::text),
|
|
586
|
+
owner_email TEXT NOT NULL DEFAULT 'local@localhost',
|
|
587
|
+
org_id TEXT
|
|
588
|
+
)`,
|
|
589
|
+
sqlite: `CREATE TABLE IF NOT EXISTS analytics_alert_incidents (
|
|
590
|
+
id TEXT PRIMARY KEY,
|
|
591
|
+
rule_id TEXT NOT NULL,
|
|
592
|
+
triggered_at TEXT NOT NULL,
|
|
593
|
+
window_start TEXT NOT NULL,
|
|
594
|
+
window_end TEXT NOT NULL,
|
|
595
|
+
threshold INTEGER NOT NULL,
|
|
596
|
+
observed_value INTEGER NOT NULL,
|
|
597
|
+
event_count INTEGER NOT NULL,
|
|
598
|
+
severity TEXT NOT NULL,
|
|
599
|
+
channels TEXT NOT NULL DEFAULT '[]',
|
|
600
|
+
sample_events TEXT NOT NULL DEFAULT '[]',
|
|
601
|
+
notification_id TEXT,
|
|
602
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
603
|
+
owner_email TEXT NOT NULL DEFAULT 'local@localhost',
|
|
604
|
+
org_id TEXT
|
|
605
|
+
)`,
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
version: 77,
|
|
610
|
+
sql: `CREATE INDEX IF NOT EXISTS analytics_alert_rules_scope_enabled_idx ON analytics_alert_rules (org_id, owner_email, enabled, updated_at)`,
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
version: 78,
|
|
614
|
+
sql: `CREATE INDEX IF NOT EXISTS analytics_alert_incidents_rule_triggered_idx ON analytics_alert_incidents (rule_id, triggered_at)`,
|
|
615
|
+
},
|
|
516
616
|
],
|
|
517
617
|
{ table: "analytics_migrations" },
|
|
518
618
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { createError, defineEventHandler, getHeader } from "h3";
|
|
4
|
+
|
|
5
|
+
// guard:allow-action-twin — external cron endpoint authenticates scheduled callers before invoking the alert action logic.
|
|
6
|
+
import { runAnalyticsAlertsOnce } from "../../../jobs/analytics-alerts";
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
var __AGENT_NATIVE_ANALYTICS_ALERT_SCHEDULED_RUNTIME__: boolean | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function productionLike(): boolean {
|
|
13
|
+
return (
|
|
14
|
+
process.env.NODE_ENV === "production" || process.env.NETLIFY === "true"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function scheduledFunctionRuntime(): boolean {
|
|
19
|
+
return globalThis.__AGENT_NATIVE_ANALYTICS_ALERT_SCHEDULED_RUNTIME__ === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function cronSecret(): string | null {
|
|
23
|
+
const secret = process.env.ANALYTICS_ALERTS_CRON_SECRET?.trim();
|
|
24
|
+
return secret ? secret : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function headerMatchesSecret(
|
|
28
|
+
header: string | undefined,
|
|
29
|
+
secret: string,
|
|
30
|
+
): boolean {
|
|
31
|
+
const expected = `Bearer ${secret}`;
|
|
32
|
+
const value = header?.trim() ?? "";
|
|
33
|
+
if (value.length !== expected.length) return false;
|
|
34
|
+
return timingSafeEqual(Buffer.from(value), Buffer.from(expected));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default defineEventHandler(async (event) => {
|
|
38
|
+
const secret = cronSecret();
|
|
39
|
+
const scheduledRuntime = scheduledFunctionRuntime();
|
|
40
|
+
if (!secret && productionLike() && !scheduledRuntime) {
|
|
41
|
+
throw createError({
|
|
42
|
+
statusCode: 503,
|
|
43
|
+
statusMessage: "ANALYTICS_ALERTS_CRON_SECRET is required",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
secret &&
|
|
49
|
+
!scheduledRuntime &&
|
|
50
|
+
!headerMatchesSecret(getHeader(event, "authorization"), secret)
|
|
51
|
+
) {
|
|
52
|
+
throw createError({
|
|
53
|
+
statusCode: 401,
|
|
54
|
+
statusMessage: "Unauthorized",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const result = await runAnalyticsAlertsOnce();
|
|
59
|
+
return { ok: true, ...result };
|
|
60
|
+
});
|
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
# Assets
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An open-source, agent-native digital asset manager (DAM) and brand-image/video
|
|
4
|
+
generation workspace — a self-hostable alternative to tools like Brandfolder and
|
|
5
|
+
Air, with generation built in.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
shots, diagrams, style briefs, generated candidates, and saved assets. The UI
|
|
7
|
-
curates the DAM; the agent handles generation, search, and iteration through
|
|
8
|
-
actions and A2A.
|
|
7
|
+
**Live app: [assets.agent-native.com](https://assets.agent-native.com)**
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Organize uploaded and generated media into libraries and folders, then route
|
|
10
|
+
image and video generation through the agent chat so every asset can be reviewed,
|
|
11
|
+
refined, and kept on-brand. Every generated asset writes an audit run with the
|
|
12
|
+
prompt, model, references, and lineage.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Brand-consistent image and video generation through the agent.
|
|
17
|
+
- Libraries, folders, and brand kits for organizing uploads and generated results.
|
|
18
|
+
- Generation presets that carry a brand style brief, prompt template, and logo policy.
|
|
19
|
+
- Pixel-perfect canonical logo compositing onto generated images.
|
|
20
|
+
- Reusable across apps: pick or generate assets from other apps over A2A and MCP.
|
|
21
|
+
- Full audit log of every generation run, exportable to CSV.
|
|
22
|
+
|
|
23
|
+
## Develop locally
|
|
24
|
+
|
|
25
|
+
Scaffold your own copy and run it:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @agent-native/core@latest create my-assets --standalone --template assets
|
|
29
|
+
cd my-assets
|
|
30
|
+
pnpm install
|
|
31
|
+
pnpm dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Full docs: [agent-native.com/docs/template-assets](https://agent-native.com/docs/template-assets).
|