@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 CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 7643
34
+ - template files: 7644
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Durable A2A workers now avoid startup migration contention so Analytics requests can complete while rollup maintenance runs separately.
@@ -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(): Promise<void> {
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 is intentional: competing cold starts fail fast instead of
157
- // holding pooled Neon connections while waiting for the winner. The
158
- // monotonic rollup upsert below keeps a live increment from being
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
- throw new Error(
167
- "Analytics rollup backfill is already running on another instance",
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;
@@ -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";
@@ -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. Pass `sql: {}` for a
72
- * run-only entry. Give run-only entries a `name`: the legacy version gate
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<void>;
85
+ run?: () => Promise<MigrationRunResult>;
76
86
  }
77
87
  /**
78
88
  * Runs a list of migrations against the configured database, gated by a
@@ -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
- if (m.run)
276
- await m.run();
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
- if (m.run)
486
- await m.run();
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
  }>>;
@@ -15,6 +15,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
@@ -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;
@@ -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 disableRecurringJobsRuntime = shouldDisableRecurringJobsRuntime();
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
- let inFlight = false;
4725
- const sweep = async () => {
4726
- if (inFlight)
4727
- return;
4728
- inFlight = true;
4729
- try {
4730
- const { redispatchUnclaimedAutomationRuns } = await import("../jobs/run-now.js");
4731
- await redispatchUnclaimedAutomationRuns();
4732
- }
4733
- catch (error) {
4734
- console.warn("[automations] queued-run sweep failed; retrying next tick:", error);
4735
- }
4736
- finally {
4737
- inFlight = false;
4738
- }
4739
- };
4740
- setTimeout(() => void sweep(), 15_000);
4741
- setInterval(() => void sweep(), 30_000);
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 {};
@@ -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";
@@ -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
- Agent-Native is deliberately composable. Most apps start with chat, add actions,
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
- ```tsx filename="app/routes/agent.tsx"
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 shared page currently provides twelve tabs across two groups:
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
- | Group | Tab | Shows |
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
- ## Automation-first app {#headless}
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
- await runAgentLoop({
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
- If your goal is "run an agent against this folder," start with the app-agent
271
- loop in that folder: scaffold the automation-first app, add actions/instructions, run
272
- `pnpm agent "..."`. That keeps the work inside the same action/runtime/state
273
- contract the app will use in production.
274
-
275
- External coding harnesses are a separate product surface for embedding Claude
276
- Code, Codex, Pi, Cursor, Mastra, or similar runtimes inside an Agent-Native app.
277
- Use them when you are building a coding-agent product, not as the default way to
278
- start a local agent-native workflow.
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
- ## Rich chat on your agent {#byo-agent}
168
+ ## Native inline UI {#native-inline-ui}
386
169
 
387
- Use this path when your agent is already built with another framework or
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
- ```tsx
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
- const runtime = createHttpAgentChatRuntime({
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
- export function SupportAgentChat() {
403
- return <AssistantChat runtime={runtime} threadId="support" />;
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
- Ready-made runtime helpers exist for OpenAI Agents, OpenAI Responses, the Claude
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
- [Native Chat UI — BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes)
416
- is the canonical home for the event shapes, the runtime helpers, and `chatUI`
417
- tool-result metadata. Start there when wiring an external agent into the chat.
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
- ## Full application {#full-application}
368
+ ## Automation-first app {#headless}
522
369
 
523
- Use the full app path when users need durable objects and workflows: forms,
524
- dashboards, calendars, inboxes, editors, documents, assets, or reports.
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
- Full apps add product UI around the same action and agent contract:
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
- - **SQL state** — app data, navigation, settings, and chat history are durable.
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
- Start from the [Chat template](/docs/template-chat) when you want a minimal app
537
- around your actions, or from a domain [template](/docs/cloneable-saas) when you
538
- want a complete product shape.
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
- ## How to choose {#how-to-choose}
516
+ ### Sharing sessions and runs {#sharing-runs}
541
517
 
542
- | If you are thinking... | Choose |
543
- | ---------------------------------------------------------------- | ------------------------- |
544
- | "I want the framework's agent, but chat should be the main UI." | Rich chat on Agent-Native |
545
- | "The action result should be a chart, table, or typed card." | Native inline UI |
546
- | "I want the agent to make an interactive control right now." | Generated inline UI |
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
- Keep the contract small: define durable operations as actions, return explicit
553
- widget results when chat needs rich UI, and add full screens only when users
554
- need to browse, compare, configure, or collaborate over persistent objects.
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
- - [**Actions**](/docs/actions) — define the operation once; every surface above calls the same one
559
- - [**Native Chat UI**](/docs/native-chat-ui) — render typed action results as tables, charts, and cards in chat
560
- - [**Generative UI**](/docs/generative-ui) — generate transient or persisted sandboxed UI inline in chat
561
- - [**Automation-First Apps**](/docs/pure-agent-apps) — the full no-browser pattern for jobs, queues, scripts, and external agents
562
- - [**External Agents**](/docs/external-agents) — connect MCP-compatible hosts to an app
563
- - [**A2A Protocol**](/docs/a2a-protocol) — call agents from other agent-native apps
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.1",
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": {