@agent-native/core 0.141.1 → 0.141.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 +1 -1
- package/corpus/templates/analytics/changelog/2026-08-05-durable-a2a-workers-avoid-startup-migration-contention.md +6 -0
- package/corpus/templates/analytics/server/plugins/db.ts +25 -8
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/migrations.d.ts +13 -3
- package/dist/db/migrations.js +19 -4
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.js +30 -21
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +2 -2
- package/docs/content/agent-surfaces.mdx +313 -290
- package/package.json +1 -1
package/corpus/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
deferMigration,
|
|
2
3
|
ensureAdditiveColumns,
|
|
3
4
|
getDialect,
|
|
4
5
|
getDbExec,
|
|
5
6
|
isPostgres,
|
|
6
7
|
runMigrations,
|
|
7
8
|
} from "@agent-native/core/db";
|
|
9
|
+
import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
|
|
8
10
|
|
|
9
11
|
// Side-effect import: ensures registerShareableResource runs on server
|
|
10
12
|
// startup so the dashboard / analysis share actions know where to dispatch.
|
|
@@ -141,7 +143,17 @@ const ANALYTICS_ROLLUP_BACKFILL_STATEMENTS = {
|
|
|
141
143
|
],
|
|
142
144
|
} as const;
|
|
143
145
|
|
|
144
|
-
async function runHistoricalAnalyticsRollupBackfill()
|
|
146
|
+
async function runHistoricalAnalyticsRollupBackfill() {
|
|
147
|
+
// Durable agent workers share this plugin bundle with the regular server,
|
|
148
|
+
// but they must not become a second migration runner for an expensive
|
|
149
|
+
// historical scan. The keep-warm/regular server path owns this work.
|
|
150
|
+
if (isInBackgroundFunctionRuntime()) {
|
|
151
|
+
console.info(
|
|
152
|
+
"[db] Deferring historical Analytics rollup backfill from durable background runtime",
|
|
153
|
+
);
|
|
154
|
+
return deferMigration();
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
const db = getDbExec();
|
|
146
158
|
|
|
147
159
|
if (isPostgres()) {
|
|
@@ -151,11 +163,12 @@ async function runHistoricalAnalyticsRollupBackfill(): Promise<void> {
|
|
|
151
163
|
);
|
|
152
164
|
}
|
|
153
165
|
|
|
154
|
-
await db.transaction(async (tx) => {
|
|
166
|
+
const result = await db.transaction(async (tx) => {
|
|
155
167
|
// Only one serverless instance should perform the expensive scan. A
|
|
156
|
-
// try-lock
|
|
157
|
-
//
|
|
158
|
-
//
|
|
168
|
+
// A try-lock keeps competing cold starts from holding pooled Neon
|
|
169
|
+
// connections while waiting for the winner. A lock loser defers without
|
|
170
|
+
// recording the migration so a failed winner can be retried safely.
|
|
171
|
+
// The monotonic rollup upsert below keeps a live increment from being
|
|
159
172
|
// overwritten if ingest commits during the historical snapshot.
|
|
160
173
|
const lockResult = await tx.execute({
|
|
161
174
|
sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
|
|
@@ -163,15 +176,16 @@ async function runHistoricalAnalyticsRollupBackfill(): Promise<void> {
|
|
|
163
176
|
});
|
|
164
177
|
const acquired = lockResult.rows[0]?.acquired;
|
|
165
178
|
if (acquired !== true && acquired !== "t") {
|
|
166
|
-
|
|
167
|
-
"Analytics rollup backfill
|
|
179
|
+
console.info(
|
|
180
|
+
"[db] Deferring historical Analytics rollup backfill; another instance owns the advisory lock",
|
|
168
181
|
);
|
|
182
|
+
return deferMigration();
|
|
169
183
|
}
|
|
170
184
|
for (const statement of ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.postgres) {
|
|
171
185
|
await tx.execute(statement);
|
|
172
186
|
}
|
|
173
187
|
});
|
|
174
|
-
return;
|
|
188
|
+
return result;
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
const statements = ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.sqlite;
|
|
@@ -1557,6 +1571,9 @@ const runAnalyticsMigrations = runMigrations(
|
|
|
1557
1571
|
*/
|
|
1558
1572
|
export default async (nitroApp: any): Promise<void> => {
|
|
1559
1573
|
await runAnalyticsMigrations(nitroApp);
|
|
1574
|
+
if (isInBackgroundFunctionRuntime()) {
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1560
1577
|
try {
|
|
1561
1578
|
if (await repairPersistedFirstPartyDashboardQueries()) {
|
|
1562
1579
|
console.info(
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
package/dist/db/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function createDb(config: DbConfig): Promise<(import("drizzle-orm
|
|
|
21
21
|
})>;
|
|
22
22
|
export type DrizzleDb = Awaited<ReturnType<typeof createDb>>;
|
|
23
23
|
export { createGetDb } from "./create-get-db.js";
|
|
24
|
-
export { runMigrations, type MigrationEntry, type MigrationSql, } from "./migrations.js";
|
|
24
|
+
export { deferMigration, MIGRATION_DEFERRED, runMigrations, type MigrationEntry, type MigrationRunResult, type MigrationSql, } from "./migrations.js";
|
|
25
25
|
export { getDbExec, createDbExec, getDatabaseUrl, getDialect, isLocalDatabase, isPostgres, intType, closeDbExec, type DbExec, type DbExecConfig, type DbExecQuery, type DbExecStatement, type Dialect, } from "./client.js";
|
|
26
26
|
export { table, text, integer, now } from "./schema.js";
|
|
27
27
|
export { ensureAdditiveColumns, type EnsureAdditiveColumnsOptions, type EnsureAdditiveColumnsResult, type EnsureAdditiveColumnsLogger, } from "./ensure-additive-columns.js";
|
package/dist/db/index.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function createDb(config) {
|
|
|
20
20
|
throw new Error(`Unsupported driver: ${config.driver}`);
|
|
21
21
|
}
|
|
22
22
|
export { createGetDb } from "./create-get-db.js";
|
|
23
|
-
export { runMigrations, } from "./migrations.js";
|
|
23
|
+
export { deferMigration, MIGRATION_DEFERRED, runMigrations, } from "./migrations.js";
|
|
24
24
|
export { getDbExec, createDbExec, getDatabaseUrl, getDialect, isLocalDatabase, isPostgres, intType, closeDbExec, } from "./client.js";
|
|
25
25
|
export { table, text, integer, now } from "./schema.js";
|
|
26
26
|
export { ensureAdditiveColumns, } from "./ensure-additive-columns.js";
|
package/dist/db/migrations.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ export type MigrationSql = string | {
|
|
|
53
53
|
postgres?: string;
|
|
54
54
|
sqlite?: string;
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* A run-only migration can defer its work without recording itself as applied.
|
|
58
|
+
* This is for serialized backfills where another instance owns the advisory
|
|
59
|
+
* lock; throwing would turn an expected loser into a startup retry storm.
|
|
60
|
+
*/
|
|
61
|
+
export declare const MIGRATION_DEFERRED: unique symbol;
|
|
62
|
+
export declare function deferMigration(): typeof MIGRATION_DEFERRED;
|
|
63
|
+
export type MigrationRunResult = void | typeof MIGRATION_DEFERRED;
|
|
56
64
|
export interface MigrationEntry {
|
|
57
65
|
version: number;
|
|
58
66
|
sql: MigrationSql;
|
|
@@ -68,11 +76,13 @@ export interface MigrationEntry {
|
|
|
68
76
|
* JS step for a backfill SQL cannot express — e.g. a repair whose value comes
|
|
69
77
|
* from application-level parsing of a stored blob. Runs BEFORE this entry's
|
|
70
78
|
* SQL and before the bookkeeping row is written, so a throw leaves the
|
|
71
|
-
* migration unrecorded and it retries on the next boot.
|
|
72
|
-
*
|
|
79
|
+
* migration unrecorded and it retries on the next boot. Return
|
|
80
|
+
* `deferMigration()` when another instance owns a serialized backfill; the
|
|
81
|
+
* entry stays pending without logging a startup failure. Pass `sql: {}` for
|
|
82
|
+
* a run-only entry. Give run-only entries a `name`: the legacy version gate
|
|
73
83
|
* would otherwise mark them applied via any later migration's MAX advance.
|
|
74
84
|
*/
|
|
75
|
-
run?: () => Promise<
|
|
85
|
+
run?: () => Promise<MigrationRunResult>;
|
|
76
86
|
}
|
|
77
87
|
/**
|
|
78
88
|
* Runs a list of migrations against the configured database, gated by a
|
package/dist/db/migrations.js
CHANGED
|
@@ -154,6 +154,15 @@ function splitSqlStatements(sql) {
|
|
|
154
154
|
out.push(tail);
|
|
155
155
|
return out;
|
|
156
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* A run-only migration can defer its work without recording itself as applied.
|
|
159
|
+
* This is for serialized backfills where another instance owns the advisory
|
|
160
|
+
* lock; throwing would turn an expected loser into a startup retry storm.
|
|
161
|
+
*/
|
|
162
|
+
export const MIGRATION_DEFERRED = Symbol("migration-deferred");
|
|
163
|
+
export function deferMigration() {
|
|
164
|
+
return MIGRATION_DEFERRED;
|
|
165
|
+
}
|
|
157
166
|
function resolveMigrationSql(sql, pg) {
|
|
158
167
|
if (typeof sql === "string")
|
|
159
168
|
return sql;
|
|
@@ -272,8 +281,11 @@ export function runMigrations(migrations, options) {
|
|
|
272
281
|
try {
|
|
273
282
|
// D1 is SQLite-compatible
|
|
274
283
|
const raw = resolveMigrationSql(m.sql, false);
|
|
275
|
-
|
|
276
|
-
|
|
284
|
+
const runResult = m.run ? await m.run() : undefined;
|
|
285
|
+
if (runResult === MIGRATION_DEFERRED) {
|
|
286
|
+
console.info(`[db] Deferred migration ${m.name ? `"${m.name}" ` : ""}v${m.version}; it remains pending for a later boot`);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
277
289
|
const recordStatements = [
|
|
278
290
|
m.name
|
|
279
291
|
? d1
|
|
@@ -482,8 +494,11 @@ export function runMigrations(migrations, options) {
|
|
|
482
494
|
// A throw here escapes to the outer handler with nothing recorded,
|
|
483
495
|
// so the entry is retried on the next boot rather than being marked
|
|
484
496
|
// applied against work that never happened.
|
|
485
|
-
|
|
486
|
-
|
|
497
|
+
const runResult = m.run ? await m.run() : undefined;
|
|
498
|
+
if (runResult === MIGRATION_DEFERRED) {
|
|
499
|
+
console.info(`[db] Deferred migration ${label}; it remains pending for a later boot`);
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
487
502
|
if (raw == null) {
|
|
488
503
|
// Dialect-gated migration with no SQL for this dialect; still mark
|
|
489
504
|
// as applied so we don't retry forever.
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
14
|
count: number;
|
|
15
15
|
updated?: undefined;
|
|
16
|
-
error?: undefined;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
count?: undefined;
|
|
20
20
|
updated: number;
|
|
21
|
-
error?: undefined;
|
|
22
21
|
ok?: undefined;
|
|
22
|
+
error?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
25
25
|
updated?: undefined;
|
|
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
28
28
|
} | {
|
|
29
29
|
count?: undefined;
|
|
30
30
|
updated?: undefined;
|
|
31
|
-
error?: undefined;
|
|
32
31
|
ok: boolean;
|
|
32
|
+
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -44,14 +44,14 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
44
44
|
summary: import("./types.js").TraceSummary;
|
|
45
45
|
spans: import("./types.js").TraceSpan[];
|
|
46
46
|
id?: undefined;
|
|
47
|
-
ok?: undefined;
|
|
48
47
|
error?: undefined;
|
|
48
|
+
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
summary?: undefined;
|
|
51
51
|
spans?: undefined;
|
|
52
52
|
id: string;
|
|
53
|
-
ok?: undefined;
|
|
54
53
|
error?: undefined;
|
|
54
|
+
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
57
57
|
spans?: undefined;
|
|
@@ -62,6 +62,6 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
62
62
|
summary?: undefined;
|
|
63
63
|
spans?: undefined;
|
|
64
64
|
id?: undefined;
|
|
65
|
-
ok: boolean;
|
|
66
65
|
error?: undefined;
|
|
66
|
+
ok: boolean;
|
|
67
67
|
}>>;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
error: string;
|
|
52
|
+
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
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
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
|
+
error?: undefined;
|
|
40
41
|
ok: boolean;
|
|
41
42
|
status: string;
|
|
42
|
-
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
+
error?: undefined;
|
|
48
49
|
ok: boolean;
|
|
49
50
|
removed: boolean;
|
|
50
|
-
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
error?: undefined;
|
|
61
62
|
ok: boolean;
|
|
62
63
|
note?: undefined;
|
|
63
|
-
error?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
67
|
note: string;
|
|
67
|
-
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ 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;
|
|
98
99
|
ok: boolean;
|
|
99
100
|
key: string;
|
|
100
|
-
error?: undefined;
|
|
101
101
|
} | {
|
|
102
|
+
error?: undefined;
|
|
102
103
|
ok: boolean;
|
|
103
104
|
removed: boolean;
|
|
104
|
-
error?: undefined;
|
|
105
105
|
}>>;
|
|
@@ -4661,7 +4661,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4661
4661
|
}
|
|
4662
4662
|
return invokeAgentChatHandler(event);
|
|
4663
4663
|
}));
|
|
4664
|
-
const
|
|
4664
|
+
const isBackgroundRuntime = isInBackgroundFunctionRuntime();
|
|
4665
|
+
const disableRecurringJobsRuntime = isBackgroundRuntime || shouldDisableRecurringJobsRuntime();
|
|
4665
4666
|
// ─── Recurring Jobs Scheduler ──────────────────────────────────────
|
|
4666
4667
|
// Poll every 60 seconds for due recurring jobs and execute them.
|
|
4667
4668
|
// Uses setInterval so it works in all deployment environments without
|
|
@@ -4720,26 +4721,28 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4720
4721
|
// A non-Netlify self-dispatch only waits for the request to leave the
|
|
4721
4722
|
// current invocation. Keep the durable history row as a retryable queue
|
|
4722
4723
|
// so a frozen serverless handoff is recovered on the next sweep.
|
|
4723
|
-
(
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4724
|
+
if (!isBackgroundRuntime) {
|
|
4725
|
+
(() => {
|
|
4726
|
+
let inFlight = false;
|
|
4727
|
+
const sweep = async () => {
|
|
4728
|
+
if (inFlight)
|
|
4729
|
+
return;
|
|
4730
|
+
inFlight = true;
|
|
4731
|
+
try {
|
|
4732
|
+
const { redispatchUnclaimedAutomationRuns } = await import("../jobs/run-now.js");
|
|
4733
|
+
await redispatchUnclaimedAutomationRuns();
|
|
4734
|
+
}
|
|
4735
|
+
catch (error) {
|
|
4736
|
+
console.warn("[automations] queued-run sweep failed; retrying next tick:", error);
|
|
4737
|
+
}
|
|
4738
|
+
finally {
|
|
4739
|
+
inFlight = false;
|
|
4740
|
+
}
|
|
4741
|
+
};
|
|
4742
|
+
setTimeout(() => void sweep(), 15_000);
|
|
4743
|
+
setInterval(() => void sweep(), 30_000);
|
|
4744
|
+
})();
|
|
4745
|
+
}
|
|
4743
4746
|
mcpInitializationPromise = initializeMcpManager().catch((err) => {
|
|
4744
4747
|
console.warn(`[mcp-client] deferred initialization failed: ${err?.message ?? err}`);
|
|
4745
4748
|
});
|
|
@@ -4750,6 +4753,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4750
4753
|
// per instance; cheap (one indexed query when no active tasks are found).
|
|
4751
4754
|
// Throttled by the same per-owner interval guard inside reconcileAgentTeamRunsForOwner.
|
|
4752
4755
|
(() => {
|
|
4756
|
+
if (isBackgroundRuntime)
|
|
4757
|
+
return;
|
|
4753
4758
|
// Track when this instance last ran the sweep so only one sweep fires
|
|
4754
4759
|
// per 2-min window even if multiple timers fire in overlapping invocations.
|
|
4755
4760
|
let lastSweep = 0;
|
|
@@ -4897,6 +4902,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4897
4902
|
// comment above for why this exists and the timing budget in
|
|
4898
4903
|
// run-store.ts's `UNCLAIMED_BACKGROUND_RUN_FAST_SWEEP_MS` doc comment.
|
|
4899
4904
|
(() => {
|
|
4905
|
+
if (isBackgroundRuntime)
|
|
4906
|
+
return;
|
|
4900
4907
|
setTimeout(() => {
|
|
4901
4908
|
(async () => {
|
|
4902
4909
|
const { UNCLAIMED_BACKGROUND_RUN_FAST_SWEEP_MS } = await import("../agent/run-store.js");
|
|
@@ -4942,6 +4949,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4942
4949
|
// still fails loud, and it survives the fast sweep having missed a row
|
|
4943
4950
|
// entirely (e.g. a restart landed between fast-sweep ticks).
|
|
4944
4951
|
(() => {
|
|
4952
|
+
if (isBackgroundRuntime)
|
|
4953
|
+
return;
|
|
4945
4954
|
let lastSweep = 0;
|
|
4946
4955
|
const SWEEP_INTERVAL_MS = 2 * 60 * 1000;
|
|
4947
4956
|
setTimeout(() => {
|
|
@@ -27,10 +27,10 @@ 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;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createServer, type CreateServerOptions, type EnvKeyConfig, } from "./create-server.js";
|
|
2
|
-
export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
|
|
2
|
+
export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, isInBackgroundFunctionRuntime, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
|
|
3
3
|
export { readBody, readBodyWithSizeLimit, streamFile, DEFAULT_CHAT_MAX_BODY_BYTES, DEFAULT_UPLOAD_MAX_FILE_BYTES, MAX_CHAT_ATTACHMENTS_PER_MESSAGE, isAllowedUploadMimeType, } from "./h3-helpers.js";
|
|
4
4
|
export { buildDeepLink, toAbsoluteOpenUrl, toDesktopOpenUrl, toVsCodeOpenUrl, OPEN_ROUTE_SUBPATH, DESKTOP_OPEN_URL, VSCODE_OPEN_URL, type DeepLinkInput, } from "./deep-link.js";
|
|
5
5
|
export { createOpenRouteHandler, type OpenRouteOptions } from "./open-route.js";
|
|
@@ -65,7 +65,7 @@ export { FeatureNotConfiguredError, hasBuilderPrivateKey, isBuilderEnvManaged, g
|
|
|
65
65
|
export { builderDesignSystemUrl, builderProjectBranchUrl, buildBuilderDesignSystemIndexFiles, createBuilderDesignSystemProxyFields, fetchBuilderDesignSystemDecodeJobStatus, fetchBuilderDesignSystemDocs, getBuilderDesignSystemsBaseUrl, hydrateBuilderDesignSystemReference, indexBuilderDesignSystem, localBuilderDesignSystemId, mimeTypeForBuilderDesignSystemFilename, parseBuilderDesignSystemProxyReference, startBuilderDesignSystemIndex, startBuilderDesignSystemUpload, type BuildBuilderDesignSystemIndexFilesOptions, type BuilderDesignSystemCodeFileInput, type BuilderDesignSystemDecodeJobStatus, type BuilderDesignSystemDocsOptions, type BuilderDesignSystemDocument, type BuilderDesignSystemHydratedReference, type BuilderDesignSystemIndexFile, type BuilderDesignSystemIndexFromSourcesOptions, type BuilderDesignSystemIndexOptions, type BuilderDesignSystemIndexResult, type BuilderDesignSystemUploadAttachment, type BuilderDesignSystemUploadSlot, type BuilderDesignSystemProxyFields, type BuilderDesignSystemProxyFieldsOptions, type BuilderDesignSystemProxyReference, type BuilderDesignSystemSourceKind, } from "./builder-design-systems.js";
|
|
66
66
|
export { getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, runBuilderAgent, type RunBuilderAgentResult, } from "./builder-browser.js";
|
|
67
67
|
export { ensureFusionContainer, sendFusionBranchMessage, pushFusionBranch, reserveFusionHostingSlug, deployFusionProject, getFusionDeploys, getFusionBranchEditorUrl, getFusionHostingUrl, type FusionBranchRef, type EnsureFusionContainerResult, type SendFusionMessageResult, } from "./fusion-app.js";
|
|
68
|
-
export { sendEmail, isEmailConfigured, getEmailProvider, type EmailAttachment, type EmailProvider, type SendEmailArgs, } from "./email.js";
|
|
68
|
+
export { sendEmail, isEmailConfigured, getEmailReadiness, getEmailProvider, type EmailAttachment, type EmailReadiness, type EmailProvider, type SendEmailArgs, } from "./email.js";
|
|
69
69
|
export { notifyActivity, runActivityNotification, resolveActivityRecipients, type ActivityDeliveryFailure, type ActivityNotificationResult, type ActivityNotificationStatus, type NotifyActivityInput, type ResolveActivityRecipientsInput, } from "./activity-notifications.js";
|
|
70
70
|
export { renderEmail, emailStrong, emailLink, type RenderEmailArgs, type RenderedEmail, type EmailCta, } from "./email-template.js";
|
|
71
71
|
export { getAppProductionUrl, getFirstPartyProdUrl } from "./app-url.js";
|
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createServer, } from "./create-server.js";
|
|
2
|
-
export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
|
|
2
|
+
export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, isInBackgroundFunctionRuntime, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
|
|
3
3
|
export { readBody, readBodyWithSizeLimit, streamFile, DEFAULT_CHAT_MAX_BODY_BYTES, DEFAULT_UPLOAD_MAX_FILE_BYTES, MAX_CHAT_ATTACHMENTS_PER_MESSAGE, isAllowedUploadMimeType, } from "./h3-helpers.js";
|
|
4
4
|
export { buildDeepLink, toAbsoluteOpenUrl, toDesktopOpenUrl, toVsCodeOpenUrl, OPEN_ROUTE_SUBPATH, DESKTOP_OPEN_URL, VSCODE_OPEN_URL, } from "./deep-link.js";
|
|
5
5
|
export { createOpenRouteHandler } from "./open-route.js";
|
|
@@ -66,7 +66,7 @@ export { FeatureNotConfiguredError, hasBuilderPrivateKey, isBuilderEnvManaged, g
|
|
|
66
66
|
export { builderDesignSystemUrl, builderProjectBranchUrl, buildBuilderDesignSystemIndexFiles, createBuilderDesignSystemProxyFields, fetchBuilderDesignSystemDecodeJobStatus, fetchBuilderDesignSystemDocs, getBuilderDesignSystemsBaseUrl, hydrateBuilderDesignSystemReference, indexBuilderDesignSystem, localBuilderDesignSystemId, mimeTypeForBuilderDesignSystemFilename, parseBuilderDesignSystemProxyReference, startBuilderDesignSystemIndex, startBuilderDesignSystemUpload, } from "./builder-design-systems.js";
|
|
67
67
|
export { getBuilderBranchProjectId, isBuilderBranchingEnabled, requestBuilderBrowserConnection, resolveBuilderBranchProjectId, resolveIsBuilderBranchingEnabled, runBuilderAgent, } from "./builder-browser.js";
|
|
68
68
|
export { ensureFusionContainer, sendFusionBranchMessage, pushFusionBranch, reserveFusionHostingSlug, deployFusionProject, getFusionDeploys, getFusionBranchEditorUrl, getFusionHostingUrl, } from "./fusion-app.js";
|
|
69
|
-
export { sendEmail, isEmailConfigured, getEmailProvider, } from "./email.js";
|
|
69
|
+
export { sendEmail, isEmailConfigured, getEmailReadiness, getEmailProvider, } from "./email.js";
|
|
70
70
|
export { notifyActivity, runActivityNotification, resolveActivityRecipients, } from "./activity-notifications.js";
|
|
71
71
|
export { renderEmail, emailStrong, emailLink, } from "./email-template.js";
|
|
72
72
|
export { getAppProductionUrl, getFirstPartyProdUrl } from "./app-url.js";
|
|
@@ -6,73 +6,13 @@ search: "agentic app rich chat native chat UI full app automation headless BYO a
|
|
|
6
6
|
|
|
7
7
|
# Agent Surfaces
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
render structured results inline, and then grow durable pages around the same
|
|
11
|
-
SQL state. The same action surface can also power embedded sidecars,
|
|
12
|
-
automation-first jobs, external agents, and custom chat runtimes.
|
|
13
|
-
|
|
14
|
-
The useful way to choose is not by protocol first. Choose the product surface
|
|
15
|
-
you want, then use the matching primitive. New here? Read
|
|
16
|
-
[Key Concepts](/docs/key-concepts) first — it defines the actions, SQL, and
|
|
17
|
-
agent-loop vocabulary this page assumes.
|
|
18
|
-
|
|
19
|
-
| Surface | Use it when | Start with |
|
|
20
|
-
| ----------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
21
|
-
| **Rich chat on Agent-Native** | You want a standalone or embedded chat backed by the built-in agent loop. | [Chat template](/docs/template-chat), `<AgentChatSurface>`, `<AssistantChat>` |
|
|
22
|
-
| **Native inline UI** | Action results should render as first-party tables, charts, cards, approvals, or compact reports in chat. | [Native Chat UI](/docs/native-chat-ui), `chatUI.renderer`, data widget helpers |
|
|
23
|
-
| **Generated inline UI** | The agent should create temporary or reusable controls, pickers, calculators, or visualizers inside chat. | [Generative UI](/docs/generative-ui), `render-inline-extension`, `create-extension` |
|
|
24
|
-
| **Full application** | Humans and agents should share durable screens, data, navigation, and collaboration. | Templates, actions, SQL state, context awareness |
|
|
25
|
-
| **Embedded sidecar** | You already have a SaaS app and want an agent beside it with page context and host commands. | `createAgentNativeEmbeddedPlugin()`, `AgentNativeEmbedded` |
|
|
26
|
-
| **Automation-first app** | Jobs, scripts, another app, or another agent should call the work directly without a browser UI. | `agent-native create --headless`, `defineAction`, `agent-native agent`, HTTP, CLI, MCP, A2A |
|
|
27
|
-
| **Rich chat on your agent** | You built the agent elsewhere and want Agent-Native's composer, transcript, tool cards, and native widgets. | `AgentChatRuntime`, `<AssistantChat runtime={runtime}>` |
|
|
28
|
-
|
|
29
|
-
Those are stages and deployment shapes, not separate products. A workflow can
|
|
30
|
-
start in chat, appear as a native table or chart, become a durable page in the
|
|
31
|
-
app, and still run from jobs, scripts, MCP, or A2A without changing the operation
|
|
32
|
-
the agent calls.
|
|
33
|
-
|
|
34
|
-
## Full-page Manage Agent {#agent-page}
|
|
35
|
-
|
|
36
|
-
When a full application needs a durable place to inspect and configure its
|
|
37
|
-
agent, mount `AgentTabsPage` at `/agent`. Current templates pair that route
|
|
38
|
-
with an app-navigation entry and pass `agentPageHref="/agent"` to
|
|
39
|
-
`AgentSidebar`; the sidebar's Resources and Settings modes can then link to the
|
|
40
|
-
full page without duplicating those flows.
|
|
9
|
+
A **surface** is the way users (or other systems) interact with your app: a chat window, a dashboard page, a background job, an API call from another agent. Agent-Native lets you mix and match these without rebuilding your core logic, because every surface runs the same underlying actions. If you're new to Agent-Native, read [Key Concepts](/docs/key-concepts) first.
|
|
41
10
|
|
|
42
|
-
|
|
43
|
-
import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
|
|
44
|
-
export default function AgentRoute() {
|
|
45
|
-
return <AgentTabsPage />;
|
|
46
|
-
}
|
|
47
|
-
```
|
|
11
|
+
## How surfaces relate to each other
|
|
48
12
|
|
|
49
|
-
The
|
|
13
|
+
The four main product shapes sit on a spectrum from most interactive to fully headless. What makes them composable is that the foundation stays the same throughout: the same actions, the same SQL database, and the same agent loop power every shape. Adding a new surface doesn't mean rewriting what's underneath — you're just adding a new way to reach the same operations.
|
|
50
14
|
|
|
51
|
-
|
|
52
|
-
| --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
-
| Resources | **Files** | The existing `ResourcesPanel` for personal or organization files |
|
|
54
|
-
| Resources | **Instructions** | Always-on AGENTS.md-style rules |
|
|
55
|
-
| Resources | **Agents** | Custom sub-agent profiles |
|
|
56
|
-
| Resources | **Memory** | Long-term recall notes |
|
|
57
|
-
| Resources | **Skills** | Reusable workflows |
|
|
58
|
-
| Resources | **Learnings** | Corrections and patterns captured over time |
|
|
59
|
-
| Resources | **Remote agents** | A2A connections to other agent-native apps — this replaces what the Connections tab used to also show |
|
|
60
|
-
| Agent | **Snapshots** | A scope preview, token budget, ordered system sections grouped by provenance/governance/source, and the latest live-thread snapshot. Renamed from "Context"; old `#context` links redirect here. |
|
|
61
|
-
| Agent | **Connections** | MCP server management only |
|
|
62
|
-
| Agent | **Automations** | Personal and organization Scheduled/Event automations with pause/resume, details, and delete flows. The stable compatibility URL remains `/agent#jobs`. |
|
|
63
|
-
| Agent | **Settings** | Agent model, API keys, limits, voice, and automation settings |
|
|
64
|
-
| Agent | **Access** | The app MCP URL, an A2A agent card when available, and shared setup guides for Claude, ChatGPT, Cursor, Claude Code, Codex, and other clients. Links to `/mcp/connect` for the full connect flow and token fallback. |
|
|
65
|
-
|
|
66
|
-
The page currently shows personal (`user`-scope) data only — there is no
|
|
67
|
-
page-level Personal/Organization toggle today. The design goal is inspectable,
|
|
68
|
-
attributable, governable context, with capability separated from access:
|
|
69
|
-
Connections describes what the app can call; Access describes how external
|
|
70
|
-
clients connect to it. The page is a thin shell over existing components,
|
|
71
|
-
actions, and access checks—not a new admin console. An organization-scoped
|
|
72
|
-
view, grants, scope editing, revocation UI, and per-iteration provenance
|
|
73
|
-
history are not provided by this page yet.
|
|
74
|
-
|
|
75
|
-
<Diagram id="doc-block-hvdxgh" title="The surface spectrum" summary={"One action surface, four product shapes — each adds UI without changing the operation underneath."}>
|
|
15
|
+
<Diagram id="doc-block-hvdxgh" title="The surface spectrum" summary={"One action surface, four product shapes, each adding UI without changing the operation underneath."}>
|
|
76
16
|
|
|
77
17
|
```html
|
|
78
18
|
<div class="diagram-spectrum">
|
|
@@ -130,176 +70,19 @@ history are not provided by this page yet.
|
|
|
130
70
|
|
|
131
71
|
</Diagram>
|
|
132
72
|
|
|
133
|
-
##
|
|
134
|
-
|
|
135
|
-
Use the automation-first path when no one needs a custom browser screen while
|
|
136
|
-
the work runs: scheduled jobs, integrations, backend workflows, CLI loops,
|
|
137
|
-
another agent, or an existing product calling into Agent-Native.
|
|
138
|
-
|
|
139
|
-
This is the shape to reach for when automation is the product surface. You send
|
|
140
|
-
a request from the terminal, Slack, email, a scheduled job, another agent, or
|
|
141
|
-
Chat — "summarize my unread emails," "post the daily metrics to Slack," "find
|
|
142
|
-
the candidates who replied last week" — and the agent acts and returns the
|
|
143
|
-
result wherever it belongs. It is still a real app, not a stateless prompt:
|
|
144
|
-
actions, auth sessions, app state, thread/run history, settings, credentials,
|
|
145
|
-
and share records all live in SQL.
|
|
146
|
-
|
|
147
|
-
Pick this pattern when:
|
|
148
|
-
|
|
149
|
-
- **The work happens in the background.** Most of the value is created while the user isn't looking — triage agents, daily-report agents, on-call responders.
|
|
150
|
-
- **The output leaves the app.** The agent posts to Slack, sends email, or updates a third-party system; there's nothing to browse in-app.
|
|
151
|
-
- **The domain is one-shot.** Research bot, summary generator, report writer — no persistent object that needs a list view.
|
|
152
|
-
- **You're prototyping an automation.** Ship the operation now; add chat or app pages when users need to inspect and steer it.
|
|
153
|
-
|
|
154
|
-
If your product is built around persistent objects users browse, pivot, and
|
|
155
|
-
share — emails, events, documents, charts — pick a [full application](#full-application)
|
|
156
|
-
or a [template](/docs/cloneable-saas) instead; those add a full UI _plus_ the agent.
|
|
157
|
-
|
|
158
|
-
### What ships in the box {#in-the-box}
|
|
159
|
-
|
|
160
|
-
An automation-first app skips dashboard work, and it is channel-agnostic from
|
|
161
|
-
day one — the same agent runs from the web, Slack, Telegram, email, and other
|
|
162
|
-
agents because everything goes through the same actions. The trade-off is there
|
|
163
|
-
is no "browse-everything-at-a-glance" view; if users need that, start from
|
|
164
|
-
[Chat](/docs/template-chat) or add a small status page or list view.
|
|
165
|
-
|
|
166
|
-
When you add the built-in Chat shell, the framework provides five management
|
|
167
|
-
surfaces you don't have to build: **Chat** (the main input), **Resources**
|
|
168
|
-
(skills, memory, instructions, sub-agents, and connected MCP servers),
|
|
169
|
-
**Automations**, **Thread history**, and **Settings**. Those are usually enough
|
|
170
|
-
— talk to it, see what it's done, configure how it behaves. Reach for
|
|
171
|
-
[Chat](/docs/template-chat) when you're ready to add that browser UI, or the
|
|
172
|
-
[Dispatch template](/docs/template-dispatch) for a workspace-style starting
|
|
173
|
-
point with Slack/Telegram, scheduled jobs, and shared secrets out of the box.
|
|
174
|
-
|
|
175
|
-
The smallest no-browser local path is a scaffold plus one action:
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
npx @agent-native/core@latest create my-agent --headless
|
|
179
|
-
cd my-agent
|
|
180
|
-
pnpm install
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Then define the durable operation:
|
|
184
|
-
|
|
185
|
-
```ts filename="actions/summarize-week.ts"
|
|
186
|
-
import { defineAction } from "@agent-native/core/action";
|
|
187
|
-
import { z } from "zod";
|
|
188
|
-
|
|
189
|
-
export default defineAction({
|
|
190
|
-
description: "Summarize this week's submissions.",
|
|
191
|
-
readOnly: true,
|
|
192
|
-
schema: z.object({ formId: z.string() }),
|
|
193
|
-
run: async ({ formId }) => {
|
|
194
|
-
return { formId, summary: "34 submissions, up 18% from last week." };
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
One action is then callable as:
|
|
200
|
-
|
|
201
|
-
- **HTTP** — `POST /_agent-native/actions/summarize-week`
|
|
202
|
-
- **CLI** — `pnpm action summarize-week --formId form_123`
|
|
203
|
-
- **App-agent CLI** — `pnpm agent "Summarize form_123"`
|
|
204
|
-
- **MCP** — from Claude, ChatGPT, Codex, Cursor, OpenCode, Copilot, and other MCP hosts
|
|
205
|
-
- **A2A** — from another agent-native app or agent peer
|
|
206
|
-
- **UI** — through `useActionQuery`, `useActionMutation`, or `callAction`
|
|
207
|
-
- **Agent tool** — from the built-in chat loop
|
|
208
|
-
|
|
209
|
-
<Endpoint id="doc-block-kdx289" title="Calling an action over HTTP" method="POST" path="/_agent-native/actions/summarize-week" summary="Invoke any action by name over HTTP" request={{
|
|
210
|
-
"contentType": "application/json",
|
|
211
|
-
"example": "{ \"formId\": \"form_123\" }"
|
|
212
|
-
}} responses={[
|
|
213
|
-
{
|
|
214
|
-
"status": "200",
|
|
215
|
-
"description": "The action's return value as JSON",
|
|
216
|
-
"example": "{ \"formId\": \"form_123\", \"summary\": \"34 submissions, up 18% from last week.\" }"
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
"status": "400",
|
|
220
|
-
"description": "Input failed schema validation"
|
|
221
|
-
}
|
|
222
|
-
]}>
|
|
223
|
-
|
|
224
|
-
Every `defineAction` is auto-mounted at `/_agent-native/actions/<name>`. The JSON body is validated against the action's zod schema before `run` executes. To call it from an external system with a long-lived bearer token, see [HTTP API](/docs/http-api).
|
|
225
|
-
|
|
226
|
-
</Endpoint>
|
|
227
|
-
|
|
228
|
-
This is not a no-database or stateless mode. The app-agent loop stores sessions,
|
|
229
|
-
threads, runs, settings, credentials, application state, and share records in
|
|
230
|
-
SQL. Local development defaults to SQLite; hosted automation-first apps should
|
|
231
|
-
use a persistent SQL database.
|
|
232
|
-
|
|
233
|
-
If you need the whole agent loop headlessly from the project folder, use:
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
pnpm agent "Summarize this week's forms."
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
If another app or script needs to call the whole agent, use
|
|
240
|
-
`agentNative.invoke("analytics", "...")` or the `agent-native invoke` CLI. That
|
|
241
|
-
keeps cross-app work on the A2A path while local work stays on actions.
|
|
242
|
-
|
|
243
|
-
Workers, jobs, integration webhooks, and custom hosts can drive the agent loop
|
|
244
|
-
directly through the server API. This is lower-level than actions — you provide
|
|
245
|
-
the engine, model, messages, tools, actions, an event sink, and an abort signal
|
|
246
|
-
yourself:
|
|
247
|
-
|
|
248
|
-
```ts
|
|
249
|
-
import { runAgentLoop } from "@agent-native/core/server";
|
|
73
|
+
## Choose a starting point
|
|
250
74
|
|
|
251
|
-
|
|
252
|
-
engine,
|
|
253
|
-
model,
|
|
254
|
-
systemPrompt,
|
|
255
|
-
tools,
|
|
256
|
-
actions,
|
|
257
|
-
messages,
|
|
258
|
-
send,
|
|
259
|
-
signal,
|
|
260
|
-
});
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
For most apps, scheduled prompts and integration webhooks already call this loop
|
|
264
|
-
for you. Reach for it directly only when building a custom no-browser host, eval
|
|
265
|
-
runner, or server-side orchestration surface — see [Server — Production agent
|
|
266
|
-
handler](/docs/server#agent-handler) for the full signature.
|
|
267
|
-
|
|
268
|
-
### Running against a folder {#folder-loop}
|
|
75
|
+
Chat is the most common entry point. Apps typically grow inline UI as output gets richer, then add full app pages when users need persistent objects to browse and share. The same actions power the buttons, scheduled jobs, and external agents that come later. Use the Embedded sidecar when adding an agent to a product you already own, or Automation-first for work that runs without a browser. Here's the full picture:
|
|
269
76
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
### Cloud repo access {#cloud-repo-access}
|
|
281
|
-
|
|
282
|
-
For cloud automation-first apps that need repository access, use the GitHub connector
|
|
283
|
-
plus token CRUD model: list repositories, search files, read files, create or
|
|
284
|
-
edit files, delete files, and revoke access through provider-scoped
|
|
285
|
-
credentials. In local development, set the target repository explicitly:
|
|
286
|
-
|
|
287
|
-
```bash
|
|
288
|
-
GITHUB_REPOSITORY=owner/repo pnpm agent "Read README.md and suggest the next action."
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
Do not treat a VM clone or long-lived sandbox checkout as the primary cloud
|
|
292
|
-
repo-access model. Sandboxes still matter for isolated code execution, but
|
|
293
|
-
repository access should be explicit, permissioned, auditable, and revocable
|
|
294
|
-
through the connector layer.
|
|
295
|
-
|
|
296
|
-
### Sharing sessions and runs {#sharing-runs}
|
|
297
|
-
|
|
298
|
-
Automation-first sessions and runs are durable objects. Shareability should be
|
|
299
|
-
phased: read/share links first, so teammates can inspect sanitized prompts,
|
|
300
|
-
outputs, and run status; permissioned writable collaboration later, so
|
|
301
|
-
continuing a run, approving actions, editing schedules, or changing
|
|
302
|
-
configuration goes through explicit access checks.
|
|
77
|
+
| Surface | Use it when | Start with |
|
|
78
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
79
|
+
| **[Rich chat](#rich-chat)** | Users talk to the agent, see tool calls, and keep a thread history. | [Chat template](/docs/template-chat), `<AgentChatSurface>` |
|
|
80
|
+
| **[Native inline UI](#native-inline-ui)** | Action results should render as tables, charts, cards, or approvals in chat. | [Native Chat UI](/docs/native-chat-ui), `chatUI.renderer` |
|
|
81
|
+
| **[Generated inline UI](#generated-inline-ui)** | The agent should create temporary or reusable controls inside chat on the fly. | [Generative UI](/docs/generative-ui), `render-inline-extension` |
|
|
82
|
+
| **[Full application](#full-application)** | Users need durable screens, shared data, navigation, and collaboration. | Templates, actions, SQL state, context awareness |
|
|
83
|
+
| **[Embedded sidecar](#embedded-sidecar)** | You already have a SaaS app and want an agent beside it with page context. | `createAgentNativeEmbeddedPlugin()`, `AgentNativeEmbedded` |
|
|
84
|
+
| **[Automation-first](#headless)** | Jobs, scripts, or other agents call the work directly with no browser UI. | `agent-native create --headless`, `defineAction`, HTTP, CLI, MCP, A2A |
|
|
85
|
+
| **[Rich chat on your agent](#byo-agent)** | You built the agent elsewhere and want Agent-Native's chat UI around it. | `AgentChatRuntime`, `<AssistantChat runtime={runtime}>` |
|
|
303
86
|
|
|
304
87
|
## Rich chat on Agent-Native {#rich-chat}
|
|
305
88
|
|
|
@@ -382,39 +165,99 @@ React widget, use [Generative UI](/docs/generative-ui): it renders sandboxed
|
|
|
382
165
|
Alpine/Tailwind UI inline, can read app state and slot context, and can send
|
|
383
166
|
selected values back to chat.
|
|
384
167
|
|
|
385
|
-
##
|
|
168
|
+
## Native inline UI {#native-inline-ui}
|
|
386
169
|
|
|
387
|
-
Use this
|
|
388
|
-
runtime and you want Agent-Native's chat UI around it. `AgentChatRuntime` is the
|
|
389
|
-
boundary: your runtime streams normalized events, and Agent-Native renders the
|
|
390
|
-
composer, transcript, tool calls, approvals, native widgets, and app layout.
|
|
170
|
+
Use this when your actions return structured data — a list of records, a chart dataset, a status summary — that should render as a real UI component inside the chat thread rather than a plain text description. You define a `chatUI` renderer on the action, and Agent-Native renders it as a first-party React component: no iframes, no separate rendering path.
|
|
391
171
|
|
|
392
|
-
|
|
393
|
-
import {
|
|
394
|
-
AssistantChat,
|
|
395
|
-
createHttpAgentChatRuntime,
|
|
396
|
-
} from "@agent-native/core/client/chat";
|
|
172
|
+
This is the right choice when the output has a clear, reusable shape that you'd design once and use across many agent responses. For controls the agent needs to create dynamically at runtime, see [Generated inline UI](#generated-inline-ui) instead.
|
|
397
173
|
|
|
398
|
-
|
|
399
|
-
endpoint: "/api/support-agent/chat",
|
|
400
|
-
});
|
|
174
|
+
See [Native Chat UI](/docs/native-chat-ui) for the full renderer API, widget library, and BYO agent runtime integration.
|
|
401
175
|
|
|
402
|
-
|
|
403
|
-
|
|
176
|
+
## Generated inline UI {#generated-inline-ui}
|
|
177
|
+
|
|
178
|
+
Use this when the agent needs to create a control that doesn't exist yet as a pre-built widget — a custom form, a picker built around the current context, a one-off calculator. Unlike native widgets, generated UI is composed by the agent at runtime from Alpine.js and Tailwind, runs sandboxed in an iframe, and can send selected values back into the chat thread.
|
|
179
|
+
|
|
180
|
+
Generated UI can be transient (rendered once and discarded) or saved as a reusable extension that persists for the user.
|
|
181
|
+
|
|
182
|
+
See [Generative UI](/docs/generative-ui) for the full API, sandbox constraints, and extension persistence model.
|
|
183
|
+
|
|
184
|
+
## Full application {#full-application}
|
|
185
|
+
|
|
186
|
+
Use the full app path when users need durable objects and workflows: forms,
|
|
187
|
+
dashboards, calendars, inboxes, editors, documents, assets, or reports.
|
|
188
|
+
|
|
189
|
+
Full apps add product UI around the same action and agent contract:
|
|
190
|
+
|
|
191
|
+
<Cards>
|
|
192
|
+
|
|
193
|
+
### SQL state
|
|
194
|
+
|
|
195
|
+
App data, navigation, settings, and chat history are all durable. The agent reads and writes the same rows the UI does.
|
|
196
|
+
|
|
197
|
+
### Context awareness
|
|
198
|
+
|
|
199
|
+
The agent knows the current route, selection, and focused object, so "edit this" always means the right thing.
|
|
200
|
+
|
|
201
|
+
### Live sync
|
|
202
|
+
|
|
203
|
+
Agent changes update the UI in real time, and UI changes update the agent's context. No polling, no refresh.
|
|
204
|
+
|
|
205
|
+
### Deep links
|
|
206
|
+
|
|
207
|
+
Action results can open the right app view directly: a chart links to the dashboard, a draft links to the inbox.
|
|
208
|
+
|
|
209
|
+
### Native chat widgets
|
|
210
|
+
|
|
211
|
+
Tables, charts, cards, approvals, and typed results render as first-party React components inline in chat.
|
|
212
|
+
|
|
213
|
+
### Generative UI and extensions
|
|
214
|
+
|
|
215
|
+
The agent can create inline controls on the fly, and save reusable mini-apps when a workflow needs to persist.
|
|
216
|
+
|
|
217
|
+
</Cards>
|
|
218
|
+
|
|
219
|
+
Start from the [Chat template](/docs/template-chat) when you want a minimal app
|
|
220
|
+
around your actions, or from a domain [template](/docs/cloneable-saas) when you
|
|
221
|
+
want a complete product shape.
|
|
222
|
+
|
|
223
|
+
### Full-page Manage Agent {#agent-page}
|
|
224
|
+
|
|
225
|
+
Every Agent-Native app eventually needs a place where users can configure their agent: set standing instructions, review what it's done, connect MCP servers, manage automations, and control access. Building that UI from scratch is a lot of work. Agent-Native ships a pre-built full-page component, `AgentTabsPage`, that covers all of it across twelve tabs.
|
|
226
|
+
|
|
227
|
+
Mount it at `/agent` in your app. Current templates pair that route with an app-navigation entry and pass `agentPageHref="/agent"` to `AgentSidebar`, so the sidebar's Resources and Settings modes can link to the full page without duplicating those flows.
|
|
228
|
+
|
|
229
|
+
```tsx filename="app/routes/agent.tsx"
|
|
230
|
+
import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
|
|
231
|
+
export default function AgentRoute() {
|
|
232
|
+
return <AgentTabsPage />;
|
|
404
233
|
}
|
|
405
234
|
```
|
|
406
235
|
|
|
407
|
-
|
|
408
|
-
Agent SDK, the Vercel AI SDK, and AG-UI, plus the normalized HTTP runtime above
|
|
409
|
-
for any other agent (Mastra, Flue, Eve, LangGraph, or a custom service). ACP is
|
|
410
|
-
not the end-user app chat or A2A transport, and Agent-Native does not currently
|
|
411
|
-
claim A2UI support. ACP is supported in one specific place — driving a local
|
|
412
|
-
coding agent (Gemini CLI, Claude Code, …) through the
|
|
413
|
-
[harness layer](/docs/harness-agents#acp), not as the chat runtime here.
|
|
236
|
+
The shared page currently provides twelve tabs across two groups:
|
|
414
237
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
238
|
+
| Group | Tab | Shows |
|
|
239
|
+
| --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
240
|
+
| Resources | **Files** | The existing `ResourcesPanel` for personal or organization files |
|
|
241
|
+
| Resources | **Instructions** | Always-on AGENTS.md-style rules |
|
|
242
|
+
| Resources | **Agents** | Custom sub-agent profiles |
|
|
243
|
+
| Resources | **Memory** | Long-term recall notes |
|
|
244
|
+
| Resources | **Skills** | Reusable workflows |
|
|
245
|
+
| Resources | **Learnings** | Corrections and patterns captured over time |
|
|
246
|
+
| Resources | **Remote agents** | A2A connections to other agent-native apps (replaces what the Connections tab used to show) |
|
|
247
|
+
| Agent | **Snapshots** | A scope preview, token budget, ordered system sections grouped by provenance/governance/source, and the latest live-thread snapshot. Renamed from "Context"; old `#context` links redirect here. |
|
|
248
|
+
| Agent | **Connections** | MCP server management only |
|
|
249
|
+
| Agent | **Automations** | Personal and organization Scheduled/Event automations with pause/resume, details, and delete flows. The stable compatibility URL remains `/agent#jobs`. |
|
|
250
|
+
| Agent | **Settings** | Agent model, API keys, limits, voice, and automation settings |
|
|
251
|
+
| Agent | **Access** | The app MCP URL, an A2A agent card when available, and shared setup guides for Claude, ChatGPT, Cursor, Claude Code, Codex, and other clients. Links to `/mcp/connect` for the full connect flow and token fallback. |
|
|
252
|
+
|
|
253
|
+
The page shows personal (`user`-scope) data only. There is no org-level toggle today. It is a thin shell over existing components and access checks, not a new admin console: Connections describes what the app can call; Access describes how external clients connect to it.
|
|
254
|
+
|
|
255
|
+
Not yet included:
|
|
256
|
+
|
|
257
|
+
- Organization-scoped view
|
|
258
|
+
- Grants and scope editing
|
|
259
|
+
- Revocation UI
|
|
260
|
+
- Per-iteration provenance history
|
|
418
261
|
|
|
419
262
|
## Embedded sidecar {#embedded-sidecar}
|
|
420
263
|
|
|
@@ -456,6 +299,10 @@ export function AppShell({ children }) {
|
|
|
456
299
|
}
|
|
457
300
|
```
|
|
458
301
|
|
|
302
|
+
### How it connects
|
|
303
|
+
|
|
304
|
+
The two pieces work as a bridge: the host app passes page context (current route, selected text, focused object) into `AgentNativeEmbedded`, and the agent sends commands back out through `onNavigate` and `onRefresh`. The server plugin handles identity — it resolves the host session so the agent acts as the right user without a separate login. Nothing in the host app needs to change; the plugin attaches Agent-Native's routes alongside your existing ones.
|
|
305
|
+
|
|
459
306
|
<Diagram id="doc-block-27ug5y" title="How the sidecar bridges to a host app" summary={"The plugin mounts Agent-Native routes server-side; the React sidecar streams page context in and host commands out."}>
|
|
460
307
|
|
|
461
308
|
```html
|
|
@@ -518,46 +365,222 @@ export function AppShell({ children }) {
|
|
|
518
365
|
See [Embedding SDK](/docs/embedding-sdk) for host auth, database isolation,
|
|
519
366
|
iframe/picker mode, and lower-level bridge APIs.
|
|
520
367
|
|
|
521
|
-
##
|
|
368
|
+
## Automation-first app {#headless}
|
|
522
369
|
|
|
523
|
-
Use the
|
|
524
|
-
|
|
370
|
+
Use the automation-first path when no one needs a custom browser screen while
|
|
371
|
+
the work runs: scheduled jobs, integrations, backend workflows, CLI loops,
|
|
372
|
+
another agent, or an existing product calling into Agent-Native.
|
|
525
373
|
|
|
526
|
-
|
|
374
|
+
This is the shape to reach for when automation is the product surface. You send a request from the terminal, Slack, email, a scheduled job, another agent, or Chat ("summarize my unread emails," "post the daily metrics to Slack," "find the candidates who replied last week") and the agent acts and returns the result wherever it belongs. It is still a real app, not a stateless prompt:
|
|
375
|
+
actions, auth sessions, app state, thread/run history, settings, credentials,
|
|
376
|
+
and share records all live in SQL.
|
|
527
377
|
|
|
528
|
-
|
|
529
|
-
- **Context awareness** — the agent knows the current route, selection, and focused object.
|
|
530
|
-
- **Live sync** — agent changes update the UI, and UI changes update the agent's context.
|
|
531
|
-
- **Deep links** — action results can open the right app view.
|
|
532
|
-
- **Native chat widgets** — tables, charts, cards, approvals, and typed results appear inline.
|
|
533
|
-
- **Generative UI and extensions** — the agent can create inline controls now
|
|
534
|
-
and save reusable mini-apps when the workflow needs to persist.
|
|
378
|
+
Pick this pattern when:
|
|
535
379
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
380
|
+
- **The work happens in the background.** Most of the value is created while the user isn't looking: triage agents, daily-report agents, on-call responders.
|
|
381
|
+
- **The output leaves the app.** The agent posts to Slack, sends email, or updates a third-party system; there's nothing to browse in-app.
|
|
382
|
+
- **The domain is one-shot.** Research bot, summary generator, report writer with no persistent object that needs a list view.
|
|
383
|
+
- **You're prototyping an automation.** Ship the operation now; add chat or app pages when users need to inspect and steer it.
|
|
384
|
+
|
|
385
|
+
If your product is built around persistent objects users browse, pivot, and share (emails, events, documents, charts), pick a [full application](#full-application) or a [template](/docs/cloneable-saas) instead; those add a full UI _plus_ the agent.
|
|
386
|
+
|
|
387
|
+
### What ships in the box {#in-the-box}
|
|
388
|
+
|
|
389
|
+
An automation-first app skips dashboard work, and it is channel-agnostic from day one. The same agent runs from the web, Slack, Telegram, email, and other agents because everything goes through the same actions. The trade-off is there is no "browse-everything-at-a-glance" view; if users need that, start from [Chat](/docs/template-chat) or add a small status page or list view.
|
|
390
|
+
|
|
391
|
+
When you add the built-in Chat shell, the framework provides five management surfaces you don't have to build: **Chat** (the main input), **Resources** (skills, memory, instructions, sub-agents, and connected MCP servers), **Automations**, **Thread history**, and **Settings**. Those are usually enough: talk to it, see what it's done, configure how it behaves. Reach for
|
|
392
|
+
[Chat](/docs/template-chat) when you're ready to add that browser UI, or the
|
|
393
|
+
[Dispatch template](/docs/template-dispatch) for a workspace-style starting
|
|
394
|
+
point with Slack/Telegram, scheduled jobs, and shared secrets out of the box.
|
|
395
|
+
|
|
396
|
+
The smallest no-browser local path is a scaffold plus one action:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
npx @agent-native/core@latest create my-agent --headless
|
|
400
|
+
cd my-agent
|
|
401
|
+
pnpm install
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Then define the durable operation:
|
|
405
|
+
|
|
406
|
+
```ts filename="actions/summarize-week.ts"
|
|
407
|
+
import { defineAction } from "@agent-native/core/action";
|
|
408
|
+
import { z } from "zod";
|
|
409
|
+
|
|
410
|
+
export default defineAction({
|
|
411
|
+
description: "Summarize this week's submissions.",
|
|
412
|
+
readOnly: true,
|
|
413
|
+
schema: z.object({ formId: z.string() }),
|
|
414
|
+
run: async ({ formId }) => {
|
|
415
|
+
return { formId, summary: "34 submissions, up 18% from last week." };
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
One action is then callable as:
|
|
421
|
+
|
|
422
|
+
- **HTTP:** `POST /_agent-native/actions/summarize-week`
|
|
423
|
+
- **CLI:** `pnpm action summarize-week --formId form_123`
|
|
424
|
+
- **App-agent CLI:** `pnpm agent "Summarize form_123"`
|
|
425
|
+
- **MCP:** from Claude, ChatGPT, Codex, Cursor, OpenCode, Copilot, and other MCP hosts
|
|
426
|
+
- **A2A:** from another agent-native app or agent peer
|
|
427
|
+
- **UI:** through `useActionQuery`, `useActionMutation`, or `callAction`
|
|
428
|
+
- **Agent tool:** from the built-in chat loop
|
|
429
|
+
|
|
430
|
+
<Endpoint id="doc-block-kdx289" title="Calling an action over HTTP" method="POST" path="/_agent-native/actions/summarize-week" summary="Invoke any action by name over HTTP" request={{
|
|
431
|
+
"contentType": "application/json",
|
|
432
|
+
"example": "{ \"formId\": \"form_123\" }"
|
|
433
|
+
}} responses={[
|
|
434
|
+
{
|
|
435
|
+
"status": "200",
|
|
436
|
+
"description": "The action's return value as JSON",
|
|
437
|
+
"example": "{ \"formId\": \"form_123\", \"summary\": \"34 submissions, up 18% from last week.\" }"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
"status": "400",
|
|
441
|
+
"description": "Input failed schema validation"
|
|
442
|
+
}
|
|
443
|
+
]}>
|
|
444
|
+
|
|
445
|
+
Every `defineAction` is auto-mounted at `/_agent-native/actions/<name>`. The JSON body is validated against the action's zod schema before `run` executes. To call it from an external system with a long-lived bearer token, see [HTTP API](/docs/http-api).
|
|
446
|
+
|
|
447
|
+
</Endpoint>
|
|
448
|
+
|
|
449
|
+
This is not a no-database or stateless mode. The app-agent loop stores sessions,
|
|
450
|
+
threads, runs, settings, credentials, application state, and share records in
|
|
451
|
+
SQL. Local development defaults to SQLite; hosted automation-first apps should
|
|
452
|
+
use a persistent SQL database.
|
|
453
|
+
|
|
454
|
+
If you need the whole agent loop headlessly from the project folder, use:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
pnpm agent "Summarize this week's forms."
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
If another app or script needs to call the whole agent, use
|
|
461
|
+
`agentNative.invoke("analytics", "...")` or the `agent-native invoke` CLI. That
|
|
462
|
+
keeps cross-app work on the A2A path while local work stays on actions.
|
|
463
|
+
|
|
464
|
+
Workers, jobs, integration webhooks, and custom hosts can drive the agent loop
|
|
465
|
+
directly through the server API. This is lower-level than actions — you provide
|
|
466
|
+
the engine, model, messages, tools, actions, an event sink, and an abort signal
|
|
467
|
+
yourself:
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
import { runAgentLoop } from "@agent-native/core/server";
|
|
471
|
+
|
|
472
|
+
await runAgentLoop({
|
|
473
|
+
engine,
|
|
474
|
+
model,
|
|
475
|
+
systemPrompt,
|
|
476
|
+
tools,
|
|
477
|
+
actions,
|
|
478
|
+
messages,
|
|
479
|
+
send,
|
|
480
|
+
signal,
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
For most apps, scheduled prompts and integration webhooks already call this loop
|
|
485
|
+
for you. Reach for it directly only when building a custom no-browser host, eval
|
|
486
|
+
runner, or server-side orchestration surface. See [Server: Production agent handler](/docs/server#agent-handler) for the full signature.
|
|
487
|
+
|
|
488
|
+
### Running against a folder {#folder-loop}
|
|
489
|
+
|
|
490
|
+
If your goal is "run an agent against this folder," start with the app-agent
|
|
491
|
+
loop in that folder: scaffold the automation-first app, add actions/instructions, run
|
|
492
|
+
`pnpm agent "..."`. That keeps the work inside the same action/runtime/state
|
|
493
|
+
contract the app will use in production.
|
|
494
|
+
|
|
495
|
+
External coding harnesses are a separate product surface for embedding Claude
|
|
496
|
+
Code, Codex, Pi, Cursor, Mastra, or similar runtimes inside an Agent-Native app.
|
|
497
|
+
Use them when you are building a coding-agent product, not as the default way to
|
|
498
|
+
start a local agent-native workflow.
|
|
499
|
+
|
|
500
|
+
### Cloud repo access {#cloud-repo-access}
|
|
501
|
+
|
|
502
|
+
For cloud automation-first apps that need repository access, use the GitHub connector
|
|
503
|
+
plus token CRUD model: list repositories, search files, read files, create or
|
|
504
|
+
edit files, delete files, and revoke access through provider-scoped
|
|
505
|
+
credentials. In local development, set the target repository explicitly:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
GITHUB_REPOSITORY=owner/repo pnpm agent "Read README.md and suggest the next action."
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Do not treat a VM clone or long-lived sandbox checkout as the primary cloud
|
|
512
|
+
repo-access model. Sandboxes still matter for isolated code execution, but
|
|
513
|
+
repository access should be explicit, permissioned, auditable, and revocable
|
|
514
|
+
through the connector layer.
|
|
539
515
|
|
|
540
|
-
|
|
516
|
+
### Sharing sessions and runs {#sharing-runs}
|
|
541
517
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
| "The agent and UI should evolve together as the product." | Full application |
|
|
548
|
-
| "I already have a SaaS app; add an agent beside it." | Embedded sidecar |
|
|
549
|
-
| "I need a callable workflow for jobs, scripts, or other agents." | Automation-first app |
|
|
550
|
-
| "I already have an agent; I need a polished chat UI for it." | Rich chat on your agent |
|
|
518
|
+
Automation-first sessions and runs are durable objects. Shareability should be
|
|
519
|
+
phased: read/share links first, so teammates can inspect sanitized prompts,
|
|
520
|
+
outputs, and run status; permissioned writable collaboration later, so
|
|
521
|
+
continuing a run, approving actions, editing schedules, or changing
|
|
522
|
+
configuration goes through explicit access checks.
|
|
551
523
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
524
|
+
## Rich chat on your agent {#byo-agent}
|
|
525
|
+
|
|
526
|
+
Use this path when your agent is already built with another framework or
|
|
527
|
+
runtime and you want Agent-Native's chat UI around it. `AgentChatRuntime` is the
|
|
528
|
+
boundary: your runtime streams normalized events, and Agent-Native renders the
|
|
529
|
+
composer, transcript, tool calls, approvals, native widgets, and app layout.
|
|
530
|
+
|
|
531
|
+
```tsx
|
|
532
|
+
import {
|
|
533
|
+
AssistantChat,
|
|
534
|
+
createHttpAgentChatRuntime,
|
|
535
|
+
} from "@agent-native/core/client/chat";
|
|
536
|
+
|
|
537
|
+
const runtime = createHttpAgentChatRuntime({
|
|
538
|
+
endpoint: "/api/support-agent/chat",
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
export function SupportAgentChat() {
|
|
542
|
+
return <AssistantChat runtime={runtime} threadId="support" />;
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Ready-made runtime helpers exist for OpenAI Agents, OpenAI Responses, the Claude
|
|
547
|
+
Agent SDK, the Vercel AI SDK, and AG-UI, plus the normalized HTTP runtime above
|
|
548
|
+
for any other agent (Mastra, Flue, Eve, LangGraph, or a custom service). ACP is
|
|
549
|
+
not the end-user app chat or A2A transport, and Agent-Native does not currently
|
|
550
|
+
claim A2UI support. ACP is supported in one specific place: driving a local
|
|
551
|
+
coding agent (Gemini CLI, Claude Code, …) through the
|
|
552
|
+
[harness layer](/docs/harness-agents#acp), not as the chat runtime here.
|
|
553
|
+
|
|
554
|
+
[Native Chat UI: BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes)
|
|
555
|
+
is the canonical home for the event shapes, the runtime helpers, and `chatUI`
|
|
556
|
+
tool-result metadata. Start there when wiring an external agent into the chat.
|
|
555
557
|
|
|
556
558
|
## What's next {#related-docs}
|
|
557
559
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
560
|
+
<Cards>
|
|
561
|
+
|
|
562
|
+
### [Actions](/docs/actions)
|
|
563
|
+
|
|
564
|
+
Define the operation once. Every surface above calls the same one.
|
|
565
|
+
|
|
566
|
+
### [Native Chat UI](/docs/native-chat-ui)
|
|
567
|
+
|
|
568
|
+
Render typed action results as tables, charts, and cards directly in chat.
|
|
569
|
+
|
|
570
|
+
### [Generative UI](/docs/generative-ui)
|
|
571
|
+
|
|
572
|
+
Generate transient or persisted sandboxed UI inline in chat.
|
|
573
|
+
|
|
574
|
+
### [Automation-First Apps](/docs/pure-agent-apps)
|
|
575
|
+
|
|
576
|
+
The full no-browser pattern for jobs, queues, scripts, and external agents.
|
|
577
|
+
|
|
578
|
+
### [External Agents](/docs/external-agents)
|
|
579
|
+
|
|
580
|
+
Connect MCP-compatible hosts to your app as a tool server.
|
|
581
|
+
|
|
582
|
+
### [A2A Protocol](/docs/a2a-protocol)
|
|
583
|
+
|
|
584
|
+
Call agents from other agent-native apps over the A2A standard.
|
|
585
|
+
|
|
586
|
+
</Cards>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.141.
|
|
3
|
+
"version": "0.141.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": {
|