@agent-native/core 0.222.2-nightly-20261029152548 → 0.222.2

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.
@@ -5,6 +5,8 @@ export type AgentRunStatusRead = {
5
5
  runId?: string;
6
6
  turnId?: string;
7
7
  terminalReason?: string;
8
+ /** A truncated run whose continuation the server has not started yet. */
9
+ awaitingRedispatch?: boolean;
8
10
  } | {
9
11
  ok: false;
10
12
  };
@@ -38,6 +38,9 @@ export async function readAgentRunStatus(input) {
38
38
  ...(typeof state.terminalReason === "string" && state.terminalReason
39
39
  ? { terminalReason: state.terminalReason }
40
40
  : {}),
41
+ ...(state.awaitingRedispatch === true
42
+ ? { awaitingRedispatch: true }
43
+ : {}),
41
44
  };
42
45
  }
43
46
  catch {
@@ -3416,6 +3416,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
3416
3416
  threadId: input.threadId,
3417
3417
  tabId: input.threadId,
3418
3418
  runId: run.runId,
3419
+ reason: "run.submitted",
3419
3420
  });
3420
3421
  return run;
3421
3422
  }
@@ -7,6 +7,10 @@ export interface AgentChatRunningEventDetail {
7
7
  tabId?: string;
8
8
  turnId?: string;
9
9
  runId?: string;
10
+ /**
11
+ * `"run.submitted"` marks the run this client just started for a submitted
12
+ * message, which carries no successor link to any earlier run.
13
+ */
10
14
  reason?: string;
11
15
  }
12
16
  export interface UseAgentChatRunningThreadsOptions {
@@ -17,7 +17,7 @@ import { RECURRING_JOBS_BUILD_MARKER_ENV_VAR, resolveRecurringJobsBuildMarker, }
17
17
  import { normalizeAppBasePath } from "../server/app-base-path.js";
18
18
  import { frameworkSessionHintCookieName, resolveAuthCookieNamespace, } from "../server/cookie-namespace.js";
19
19
  import { resolveAgentNativeBuildId } from "../shared/build-id.js";
20
- import { DEFAULT_SPECULATION_RULES_PATH, resolveChunkRecoveryCacheHeaders, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, SSR_QUERY_CACHE_KEY_HEADER, } from "../shared/cache-control.js";
20
+ import { CHUNK_RECOVERY_BROWSER_CACHE_CONTROL, DEFAULT_SPECULATION_RULES_PATH, DISABLED_SSR_CACHE_HEADERS, resolveChunkRecoveryCacheHeaders, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, resolveSsrCachePolicy, SSR_CACHEABLE_ERROR_STATUSES, SSR_MAX_AGE_HEADER, SSR_QUERY_CACHE_KEY_HEADER, ssrRouteCacheLimits, } from "../shared/cache-control.js";
21
21
  import { EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, } from "../shared/embed-auth.js";
22
22
  import { FRAMEWORK_INTERNAL_ROUTE_PREFIX, normalizeFrameworkRoutePrefix, toPublicFrameworkPath, } from "../shared/framework-route-prefix.js";
23
23
  import { mcpEmbedStaticAssetRouteRules } from "../shared/mcp-embed-headers.js";
@@ -1090,6 +1090,7 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
1090
1090
  const ssrCacheHeaders = resolveSsrCacheHeaders();
1091
1091
  const chunkRecoveryCacheHeaders = resolveChunkRecoveryCacheHeaders(ssrCacheHeaders);
1092
1092
  const ssrCacheKeyHeaders = resolveSsrCacheKeyHeaders();
1093
+ const ssrRouteLimits = ssrRouteCacheLimits(resolveSsrCachePolicy());
1093
1094
  const ssrAuthRedirectCookieName = frameworkSessionHintCookieName(resolveAuthCookieNamespace().frameworkCookieName);
1094
1095
  const hasActions = actions.length > 0;
1095
1096
  const routeImports = [];
@@ -1806,6 +1807,13 @@ const SSR_CACHE_HEADERS = ${JSON.stringify(ssrCacheHeaders)};
1806
1807
  const CHUNK_RECOVERY_ALIAS_CACHE_HEADERS = ${JSON.stringify(chunkRecoveryCacheHeaders)};
1807
1808
  const SSR_CACHE_KEY_HEADERS = ${JSON.stringify(ssrCacheKeyHeaders)};
1808
1809
  const SSR_QUERY_CACHE_KEY_HEADER = ${JSON.stringify(SSR_QUERY_CACHE_KEY_HEADER)};
1810
+ const SSR_MAX_AGE_HEADER = ${JSON.stringify(SSR_MAX_AGE_HEADER)};
1811
+ // The longest windows a route's SSR_MAX_AGE_HEADER can ask for; null when the
1812
+ // deployment stores nothing.
1813
+ const SSR_ROUTE_CACHE_LIMITS = ${JSON.stringify(ssrRouteLimits)};
1814
+ const SSR_UNSTORED_CACHE_HEADERS = ${JSON.stringify(DISABLED_SSR_CACHE_HEADERS)};
1815
+ const SSR_ROUTE_BROWSER_CACHE_CONTROL = ${JSON.stringify(CHUNK_RECOVERY_BROWSER_CACHE_CONTROL)};
1816
+ const SSR_CACHEABLE_ERROR_STATUSES = new Set(${JSON.stringify(SSR_CACHEABLE_ERROR_STATUSES)});
1809
1817
  const CHUNK_RECOVERY_PATH_SUFFIX = ${JSON.stringify(CHUNK_RECOVERY_PATH_SUFFIX)};
1810
1818
  const CHUNK_RECOVERY_QUERY_PARAM = ${JSON.stringify(CHUNK_RECOVERY_QUERY_PARAM)};
1811
1819
  const CHUNK_RECOVERY_QUERY_VALUE = ${JSON.stringify(CHUNK_RECOVERY_QUERY_VALUE)};
@@ -1874,12 +1882,40 @@ function injectDefaultSocialImageMeta(html, imageUrl) {
1874
1882
  }
1875
1883
 
1876
1884
  function isSsrHtmlOrDataResponse(headers, status, pathname) {
1877
- if (status < 200 || status >= 400) return false;
1885
+ if (status < 200) return false;
1886
+ if (status >= 400 && !SSR_CACHEABLE_ERROR_STATUSES.has(status)) return false;
1878
1887
  const contentType = (headers.get("content-type") || "").toLowerCase();
1879
1888
  if (contentType.includes("text/html")) return true;
1880
1889
  return pathname.endsWith(".data") && contentType.includes("text/x-script");
1881
1890
  }
1882
1891
 
1892
+ // Mirror parseSsrMaxAge and ssrCacheHeadersWithin in shared/cache-control.ts;
1893
+ // build.spec.ts checks this worker against them.
1894
+ function parseSsrMaxAge(raw) {
1895
+ if (raw == null) return null;
1896
+ const value = raw.trim();
1897
+ return /^\\d+$/.test(value) ? Number(value) : 0;
1898
+ }
1899
+
1900
+ function sharedWithin(window, seconds) {
1901
+ return (
1902
+ "s-maxage=" +
1903
+ Math.min(seconds, window.maxAge) +
1904
+ ", stale-while-revalidate=" +
1905
+ Math.min(seconds, window.stale)
1906
+ );
1907
+ }
1908
+
1909
+ function ssrCacheHeadersWithin(seconds) {
1910
+ if (!SSR_ROUTE_CACHE_LIMITS || seconds <= 0) return SSR_UNSTORED_CACHE_HEADERS;
1911
+ return {
1912
+ "cache-control": SSR_ROUTE_BROWSER_CACHE_CONTROL,
1913
+ "cdn-cache-control": "public, " + sharedWithin(SSR_ROUTE_CACHE_LIMITS.cdn, seconds),
1914
+ "netlify-cdn-cache-control":
1915
+ "public, durable, " + sharedWithin(SSR_ROUTE_CACHE_LIMITS.netlify, seconds),
1916
+ };
1917
+ }
1918
+
1883
1919
  /**
1884
1920
  * Apply the SSR cache policy to the response headers.
1885
1921
  *
@@ -1891,6 +1927,8 @@ function applyDefaultSsrCacheHeader(headers, status, pathname, isRecoveryAlias =
1891
1927
  const varyByQuery =
1892
1928
  (headers.get(SSR_QUERY_CACHE_KEY_HEADER) || "").trim().toLowerCase() === "query";
1893
1929
  headers.delete(SSR_QUERY_CACHE_KEY_HEADER);
1930
+ const routeMaxAge = parseSsrMaxAge(headers.get(SSR_MAX_AGE_HEADER));
1931
+ headers.delete(SSR_MAX_AGE_HEADER);
1894
1932
  if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;
1895
1933
 
1896
1934
  headers.delete("set-cookie");
@@ -1907,7 +1945,9 @@ function applyDefaultSsrCacheHeader(headers, status, pathname, isRecoveryAlias =
1907
1945
  else headers.delete("vary");
1908
1946
  }
1909
1947
 
1910
- for (const [name, value] of Object.entries(SSR_CACHE_HEADERS)) {
1948
+ const cacheHeaders =
1949
+ routeMaxAge === null ? SSR_CACHE_HEADERS : ssrCacheHeadersWithin(routeMaxAge);
1950
+ for (const [name, value] of Object.entries(cacheHeaders)) {
1911
1951
  headers.set(name, value);
1912
1952
  }
1913
1953
  const netlifyVary = varyByQuery
@@ -104,6 +104,11 @@ declare const messages: {
104
104
  composer: {
105
105
  chatgptManageUsage: string;
106
106
  chatgptPlanUsing: string;
107
+ newChatPlaceholder: string;
108
+ };
109
+ setup: {
110
+ connectBuilder: string;
111
+ connectPlaceholder: string;
107
112
  };
108
113
  error: {
109
114
  chatgptPlanUsageLimit: string;
@@ -28,6 +28,11 @@ const messages = {
28
28
  composer: {
29
29
  chatgptManageUsage: "Manage usage",
30
30
  chatgptPlanUsing: "Using ChatGPT plan",
31
+ newChatPlaceholder: "Ask anything",
32
+ },
33
+ setup: {
34
+ connectBuilder: "Connect Builder.io",
35
+ connectPlaceholder: "Connect AI to start chatting...",
31
36
  },
32
37
  error: {
33
38
  chatgptPlanUsageLimit: "Your ChatGPT plan usage limit has been reached.",
@@ -2,7 +2,7 @@ import { defineEventHandler } from "h3";
2
2
  import { createRequestHandler } from "react-router";
3
3
  import { getAppConfig, resolveAppHomePath } from "../app-config/index.js";
4
4
  import { isMcpPublicPath } from "../mcp/route-paths.js";
5
- import { DEFAULT_SPECULATION_RULES_PATH, resolveChunkRecoveryCacheHeaders, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, SSR_QUERY_CACHE_KEY_HEADER, } from "../shared/cache-control.js";
5
+ import { DEFAULT_SPECULATION_RULES_PATH, parseSsrMaxAge, resolveChunkRecoveryCacheHeaders, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, resolveSsrCachePolicy, SSR_CACHEABLE_ERROR_STATUSES, SSR_MAX_AGE_HEADER, SSR_QUERY_CACHE_KEY_HEADER, ssrCacheHeadersWithin, } from "../shared/cache-control.js";
6
6
  import { CHUNK_RECOVERY_PATH_SUFFIX, isLegacyChunkRecoveryRequest, } from "../shared/route-chunk-recovery-bootstrap.js";
7
7
  import { AGENT_NATIVE_SOCIAL_IMAGE_ALT, AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT, AGENT_NATIVE_SOCIAL_IMAGE_PATH, AGENT_NATIVE_SOCIAL_IMAGE_TYPE, AGENT_NATIVE_SOCIAL_IMAGE_WIDTH, withAgentNativeSocialImageCacheBuster, } from "../shared/social-meta.js";
8
8
  import { getSsrAuthRedirectScript } from "../shared/ssr-auth-redirect.js";
@@ -186,7 +186,7 @@ function injectDefaultSocialImageMeta(html, imageUrl) {
186
186
  return html;
187
187
  return html.slice(0, headCloseIdx) + tags.join("") + html.slice(headCloseIdx);
188
188
  }
189
- const CACHEABLE_ERROR_STATUSES = new Set([404, 410]);
189
+ const CACHEABLE_ERROR_STATUSES = new Set(SSR_CACHEABLE_ERROR_STATUSES);
190
190
  function isSsrHtmlOrDataResponse(headers, status, pathname) {
191
191
  if (status < 200)
192
192
  return false;
@@ -227,6 +227,11 @@ function isSsrHtmlOrDataResponse(headers, status, pathname) {
227
227
  * │ PER-USER response variation — no `private`, no `Vary: Cookie`, and no │
228
228
  * │ request-specific content. Recovery keeps a bounded alias and never adds │
229
229
  * │ arbitrary query-key variation to the normally cached shell. │
230
+ * │ │
231
+ * │ A route whose content can change or be taken down may SHORTEN its own │
232
+ * │ window with `SSR_MAX_AGE_HEADER`: one duration for every visitor of that │
233
+ * │ route, never longer than the deployment's, so it can't personalize the │
234
+ * │ shell or poison a shared key. │
230
235
  * └──────────────────────────────────────────────────────────────────────────┘
231
236
  *
232
237
  * The same sharing rule governs any DIAGNOSTIC header on this response. A
@@ -240,6 +245,8 @@ function isSsrHtmlOrDataResponse(headers, status, pathname) {
240
245
  function applyDefaultSsrCacheHeader(headers, status, pathname, isRecoveryAlias = false, isLegacyRecovery = false) {
241
246
  const responseRequestsQueryVary = headers.get(SSR_QUERY_CACHE_KEY_HEADER)?.trim().toLowerCase() === "query";
242
247
  headers.delete(SSR_QUERY_CACHE_KEY_HEADER);
248
+ const routeMaxAge = parseSsrMaxAge(headers.get(SSR_MAX_AGE_HEADER));
249
+ headers.delete(SSR_MAX_AGE_HEADER);
243
250
  if (!isSsrHtmlOrDataResponse(headers, status, pathname))
244
251
  return;
245
252
  // Current recovery uses one fixed path alias. Still-deployed clients use the
@@ -267,7 +274,10 @@ function applyDefaultSsrCacheHeader(headers, status, pathname, isRecoveryAlias =
267
274
  else
268
275
  headers.delete("vary");
269
276
  }
270
- for (const [name, value] of Object.entries(resolveSsrCacheHeaders())) {
277
+ const cacheHeaders = routeMaxAge === null
278
+ ? resolveSsrCacheHeaders()
279
+ : ssrCacheHeadersWithin(resolveSsrCachePolicy(), routeMaxAge);
280
+ for (const [name, value] of Object.entries(cacheHeaders)) {
271
281
  headers.set(name, value);
272
282
  }
273
283
  const cacheKeyHeaders = resolveSsrCacheKeyHeaders(undefined, {
@@ -10,6 +10,20 @@ export declare const DEFAULT_SSR_CACHE_HEADERS: {
10
10
  };
11
11
  export declare const SSR_HTML_CONTENT_TYPE = "text/html; charset=utf-8";
12
12
  export declare const SSR_QUERY_CACHE_KEY_HEADER = "x-agent-native-ssr-key";
13
+ /**
14
+ * Response header a route sets to the most seconds its SSR response may be
15
+ * kept anywhere, for a page whose content can change or be taken down (a
16
+ * public share page, say). The SSR handler reads and removes it. It only ever
17
+ * shortens the deployment's policy, and it names one duration for every
18
+ * visitor of the route, so it can't vary the shell per request or per user.
19
+ * `0` means don't store it at all.
20
+ */
21
+ export declare const SSR_MAX_AGE_HEADER = "x-agent-native-ssr-max-age";
22
+ /**
23
+ * Error statuses whose SSR HTML/`.data` answer still gets the shared cache
24
+ * policy: a missing or removed page is the same for every visitor.
25
+ */
26
+ export declare const SSR_CACHEABLE_ERROR_STATUSES: readonly number[];
13
27
  export type SsrHtmlContentTypeOptions = {
14
28
  varyByQuery?: boolean;
15
29
  };
@@ -32,6 +46,39 @@ export type SsrCachePolicy = {
32
46
  export type SsrCacheHeaders = Record<"cache-control" | "cdn-cache-control" | "netlify-cdn-cache-control", string>;
33
47
  export declare function parseSsrCacheSetting(raw: string | undefined | null): SsrCachePolicy;
34
48
  export declare function ssrCacheHeadersForPolicy(policy: SsrCachePolicy): SsrCacheHeaders;
49
+ /**
50
+ * The route's `SSR_MAX_AGE_HEADER` value in seconds, or null when unset. A
51
+ * value that isn't whole seconds reads as 0: the route asked for a shorter
52
+ * window, and falling back to the deployment's would keep a page it takes
53
+ * down for as long as that window.
54
+ */
55
+ export declare function parseSsrMaxAge(raw: string | null | undefined): number | null;
56
+ export type SsrSharedWindow = {
57
+ maxAge: number;
58
+ stale: number;
59
+ };
60
+ export type SsrRouteCacheLimits = {
61
+ cdn: SsrSharedWindow;
62
+ netlify: SsrSharedWindow;
63
+ };
64
+ /**
65
+ * The longest shared-cache windows a route's `SSR_MAX_AGE_HEADER` can ask for
66
+ * under `policy`, read from the headers the deployment would send, one per CDN
67
+ * header since they differ. Null when the deployment stores nothing. The
68
+ * generated edge worker inlines these at build time.
69
+ */
70
+ export declare function ssrRouteCacheLimits(policy: SsrCachePolicy): SsrRouteCacheLimits | null;
71
+ /**
72
+ * The deployment's policy with every window cut to at most `seconds`, for a
73
+ * route that set `SSR_MAX_AGE_HEADER`. Browsers revalidate on each visit, so
74
+ * shared caches alone bound how old a copy gets: fresh for `seconds`, then
75
+ * served once more while it refreshes, so never more than twice `seconds`
76
+ * after the render. Nothing is served stale on origin errors either, since a
77
+ * page taken down must not come back while origin is failing.
78
+ */
79
+ export declare function ssrCacheHeadersWithin(policy: SsrCachePolicy, seconds: number): SsrCacheHeaders;
80
+ /** The deployment-wide policy from `AGENT_NATIVE_SSR_CACHE`. */
81
+ export declare function resolveSsrCachePolicy(env?: Record<string, string | undefined>): SsrCachePolicy;
35
82
  export declare function resolveSsrCacheHeaders(env?: Record<string, string | undefined>): Readonly<SsrCacheHeaders>;
36
83
  export declare function resolveChunkRecoveryCacheHeaders(cacheHeaders?: Pick<SsrCacheHeaders, "cache-control">): Readonly<SsrCacheHeaders>;
37
84
  export declare function resolveSsrNetlifyQueryVary(varyByQuery?: boolean, varyByLegacyRecovery?: boolean): string;
@@ -11,6 +11,20 @@ export const DEFAULT_SSR_CACHE_HEADERS = {
11
11
  };
12
12
  export const SSR_HTML_CONTENT_TYPE = "text/html; charset=utf-8";
13
13
  export const SSR_QUERY_CACHE_KEY_HEADER = "x-agent-native-ssr-key";
14
+ /**
15
+ * Response header a route sets to the most seconds its SSR response may be
16
+ * kept anywhere, for a page whose content can change or be taken down (a
17
+ * public share page, say). The SSR handler reads and removes it. It only ever
18
+ * shortens the deployment's policy, and it names one duration for every
19
+ * visitor of the route, so it can't vary the shell per request or per user.
20
+ * `0` means don't store it at all.
21
+ */
22
+ export const SSR_MAX_AGE_HEADER = "x-agent-native-ssr-max-age";
23
+ /**
24
+ * Error statuses whose SSR HTML/`.data` answer still gets the shared cache
25
+ * policy: a missing or removed page is the same for every visitor.
26
+ */
27
+ export const SSR_CACHEABLE_ERROR_STATUSES = [404, 410];
14
28
  export function withSsrHtmlContentType(response, options = {}) {
15
29
  response.headers.set("content-type", SSR_HTML_CONTENT_TYPE);
16
30
  if (options.varyByQuery) {
@@ -73,8 +87,84 @@ export function ssrCacheHeadersForPolicy(policy) {
73
87
  "netlify-cdn-cache-control": netlifyControl,
74
88
  };
75
89
  }
90
+ /**
91
+ * The route's `SSR_MAX_AGE_HEADER` value in seconds, or null when unset. A
92
+ * value that isn't whole seconds reads as 0: the route asked for a shorter
93
+ * window, and falling back to the deployment's would keep a page it takes
94
+ * down for as long as that window.
95
+ */
96
+ export function parseSsrMaxAge(raw) {
97
+ if (raw == null)
98
+ return null;
99
+ const value = raw.trim();
100
+ if (!/^\d+$/.test(value))
101
+ return 0;
102
+ return Math.min(Number(value), MAX_SSR_CACHE_SECONDS);
103
+ }
104
+ function directiveSeconds(control, name) {
105
+ const match = new RegExp(`(?:^|,)\\s*${name}=(\\d+)\\s*(?:,|$)`, "i").exec(control);
106
+ return match ? Number(match[1]) : null;
107
+ }
108
+ function sharedWindow(control) {
109
+ return {
110
+ maxAge: directiveSeconds(control, "s-maxage") ??
111
+ directiveSeconds(control, "max-age") ??
112
+ 0,
113
+ stale: directiveSeconds(control, "stale-while-revalidate") ?? 0,
114
+ };
115
+ }
116
+ /**
117
+ * The longest shared-cache windows a route's `SSR_MAX_AGE_HEADER` can ask for
118
+ * under `policy`, read from the headers the deployment would send, one per CDN
119
+ * header since they differ. Null when the deployment stores nothing. The
120
+ * generated edge worker inlines these at build time.
121
+ */
122
+ export function ssrRouteCacheLimits(policy) {
123
+ if (policy.kind === "disabled")
124
+ return null;
125
+ const headers = ssrCacheHeadersForPolicy(policy);
126
+ return {
127
+ cdn: sharedWindow(headers["cdn-cache-control"]),
128
+ netlify: sharedWindow(headers["netlify-cdn-cache-control"]),
129
+ };
130
+ }
131
+ function sharedWithin(window, seconds) {
132
+ return (`s-maxage=${Math.min(seconds, window.maxAge)}, ` +
133
+ `stale-while-revalidate=${Math.min(seconds, window.stale)}`);
134
+ }
135
+ /**
136
+ * The deployment's policy with every window cut to at most `seconds`, for a
137
+ * route that set `SSR_MAX_AGE_HEADER`. Browsers revalidate on each visit, so
138
+ * shared caches alone bound how old a copy gets: fresh for `seconds`, then
139
+ * served once more while it refreshes, so never more than twice `seconds`
140
+ * after the render. Nothing is served stale on origin errors either, since a
141
+ * page taken down must not come back while origin is failing.
142
+ */
143
+ export function ssrCacheHeadersWithin(policy, seconds) {
144
+ const limits = ssrRouteCacheLimits(policy);
145
+ if (!limits || seconds <= 0)
146
+ return { ...DISABLED_SSR_CACHE_HEADERS };
147
+ return {
148
+ "cache-control": CHUNK_RECOVERY_BROWSER_CACHE_CONTROL,
149
+ "cdn-cache-control": `public, ${sharedWithin(limits.cdn, seconds)}`,
150
+ "netlify-cdn-cache-control": `public, durable, ${sharedWithin(limits.netlify, seconds)}`,
151
+ };
152
+ }
76
153
  let memoizedRaw;
77
154
  let memoizedHeaders;
155
+ let memoizedPolicyRaw;
156
+ let memoizedPolicy;
157
+ /** The deployment-wide policy from `AGENT_NATIVE_SSR_CACHE`. */
158
+ export function resolveSsrCachePolicy(env = typeof process === "undefined"
159
+ ? {}
160
+ : process.env) {
161
+ const raw = env[SSR_CACHE_ENV_VAR];
162
+ if (memoizedPolicy && raw === memoizedPolicyRaw)
163
+ return memoizedPolicy;
164
+ memoizedPolicyRaw = raw;
165
+ memoizedPolicy = parseSsrCacheSetting(raw);
166
+ return memoizedPolicy;
167
+ }
78
168
  export function resolveSsrCacheHeaders(env = typeof process === "undefined"
79
169
  ? {}
80
170
  : process.env) {
@@ -22,7 +22,7 @@ export { formatAgentDesignSystemContext, loadAgentDesignSystemContext, type Agen
22
22
  export { formatHtmlStyleSummary, summarizeHtmlStyles, type HtmlStyleFragment, type HtmlStyleSummary, type HtmlStyleValue, } from "./html-style-summary.js";
23
23
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
24
24
  export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_LANE_REDIRECT_QUERY_PARAM, BETA_LANE_RETURN_STORAGE_KEY, BETA_LANE_RETURNED_STORAGE_KEY, BETA_REDIRECT_DURATION_MS, BETA_REDIRECT_STORAGE_KEY, BETA_REDIRECT_SIGN_OUT_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildAutomaticBetaRedirectUrl, ENVIRONMENT_BETA_HOSTS, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "./environment-lanes.js";
25
- export { SSR_HTML_CONTENT_TYPE, SSR_QUERY_CACHE_KEY_HEADER, type SsrHtmlContentTypeOptions, withSsrHtmlContentType, } from "./cache-control.js";
25
+ export { SSR_HTML_CONTENT_TYPE, SSR_MAX_AGE_HEADER, SSR_QUERY_CACHE_KEY_HEADER, type SsrHtmlContentTypeOptions, withSsrHtmlContentType, } from "./cache-control.js";
26
26
  export { SURFACE_HIDDEN_FLAG, SURFACE_VISIBILITY_EVENT, addSurfaceVisibilityListener, buildSurfaceVisibilityScript, isHostSurfaceHidden, isSurfaceHidden, } from "./surface-visibility.js";
27
27
  export { AGENT_NATIVE_DOCS_ORIGIN, docsUrl, type DocsUrlOptions, } from "./docs-url.js";
28
28
  export { buildRuntimeConfigPrompt, formatRuntimeConfigReport, getRuntimeConfigReport, parseRuntimeConfigReport, runtimeConfigRequirementsFromSearchParams, type RuntimeConfigEnvironment, type RuntimeConfigIssue, type RuntimeConfigIssueCode, type RuntimeConfigIssueSeverity, type RuntimeConfigPhase, type RuntimeConfigReport, type RuntimeConfigRequirements, } from "./runtime-config.js";
@@ -22,7 +22,7 @@ export { formatAgentDesignSystemContext, loadAgentDesignSystemContext, } from ".
22
22
  export { formatHtmlStyleSummary, summarizeHtmlStyles, } from "./html-style-summary.js";
23
23
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
24
24
  export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_LANE_REDIRECT_QUERY_PARAM, BETA_LANE_RETURN_STORAGE_KEY, BETA_LANE_RETURNED_STORAGE_KEY, BETA_REDIRECT_DURATION_MS, BETA_REDIRECT_STORAGE_KEY, BETA_REDIRECT_SIGN_OUT_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildAutomaticBetaRedirectUrl, ENVIRONMENT_BETA_HOSTS, resolveEnvironmentTargets, } from "./environment-lanes.js";
25
- export { SSR_HTML_CONTENT_TYPE, SSR_QUERY_CACHE_KEY_HEADER, withSsrHtmlContentType, } from "./cache-control.js";
25
+ export { SSR_HTML_CONTENT_TYPE, SSR_MAX_AGE_HEADER, SSR_QUERY_CACHE_KEY_HEADER, withSsrHtmlContentType, } from "./cache-control.js";
26
26
  export { SURFACE_HIDDEN_FLAG, SURFACE_VISIBILITY_EVENT, addSurfaceVisibilityListener, buildSurfaceVisibilityScript, isHostSurfaceHidden, isSurfaceHidden, } from "./surface-visibility.js";
27
27
  export { AGENT_NATIVE_DOCS_ORIGIN, docsUrl, } from "./docs-url.js";
28
28
  export { buildRuntimeConfigPrompt, formatRuntimeConfigReport, getRuntimeConfigReport, parseRuntimeConfigReport, runtimeConfigRequirementsFromSearchParams, } from "./runtime-config.js";
@@ -163,9 +163,21 @@ checkout, or the expected worktree is missing, create one
163
163
  to stop.
164
164
 
165
165
  Window: roots posted in the last 5 days (or since the timestamp or permalink
166
- the invocation gives), for new reports and open items alike. Older threads
167
- are left as they are, except reports listed under `Reports` in a sweep PR
168
- opened or merged in that window: those always get their close-out.
166
+ the invocation gives), for new reports and open items alike. An older thread
167
+ is in the window too when we have already replied in it and a person has
168
+ replied since the window began: a channel read only shows roots, so search for
169
+ recent thread replies (`in:<#channel> is:thread after:<date>`) to find them.
170
+ On 10/29 a teammate's vote and question sat unanswered under a 3-week-old
171
+ root. Other older threads are left as they are, except reports listed under
172
+ `Reports` in a sweep PR opened or merged in that window: those always get
173
+ their close-out.
174
+
175
+ Reading a source is a good job for a helper: give each source below its own
176
+ helper on the cheapest model (Luna in Codex, Haiku in Claude Code), ask for a
177
+ short list of candidates with links, and close it as soon as it reports. On
178
+ 10/29 the three source reads cost $0.44 together; reusing those same helpers
179
+ for fixes afterwards cost $6.36, because each new job paid for its whole
180
+ growing history.
169
181
 
170
182
  - **Read the channels.** Use `slack_read_channel` with
171
183
  `response_format: detailed` (paginate) back 5 days. Its output shows
@@ -250,8 +262,9 @@ apart from Steve's own 👀.
250
262
  ## 3. Fix as many as you can
251
263
 
252
264
  - Group related reports so one cause gets one fix. Give each independent
253
- group a subagent on a cheaper model with disjoint files. The main thread
254
- plans, reviews diffs, and integrates.
265
+ group a fresh subagent on your own model and effort, with disjoint files,
266
+ or fix it in the main thread. Never hand a finished helper a new job; start
267
+ a fresh one. The main thread plans, reviews diffs, and integrates.
255
268
  - **Reproduce before you edit.** Get the failure on screen first: a test
256
269
  that fails, the action returning the wrong thing, a stack trace, or the
257
270
  flow broken in a signed-in local app. Reading code is not an attempt:
@@ -274,9 +287,12 @@ apart from Steve's own 👀.
274
287
  green, stop editing and capture the real failure first (stack, logs, the
275
288
  reporter's actual data). If that doesn't settle it within about 30
276
289
  minutes, it becomes 🎫 with one line on what you learned.
277
- - **Second opinion before the PR.** Give a fresh subagent only the report
278
- text, the diff, and the proof, and ask: does the check exercise what the
279
- reporter saw, and is this the cause or a symptom? Fix what it finds.
290
+ - **Second opinion before the PR, every time.** Give a fresh subagent on
291
+ your own model only the report text, the diff, and the proof, and ask: does
292
+ the check exercise what the reporter saw, is this the cause or a symptom,
293
+ and what edge case breaks it? Fix what it finds before you open the PR. The
294
+ 10/29 run skipped this, and PR review found the edge cases one round at a
295
+ time instead.
280
296
  - **Keep fixes small.** Each report's fix goes in its own commit so a bad
281
297
  one can be reverted alone. A fix that grows past about 300 lines, or into
282
298
  a new subsystem (timeouts, cancellation, retries) the reporter didn't ask
@@ -293,6 +309,19 @@ PR per item. End the PR body with a
293
309
  Slack thread, or the Sentry, Analytics, or GitHub issue. That list is the
294
310
  only set of threads a later `/ship` may close out as fixed.
295
311
 
312
+ Wait for CI with one blocking command, such as
313
+ `gh pr checks <number> --watch --required --interval 60`, and don't post a
314
+ status line while it runs: every turn re-sends your whole context, and on
315
+ 10/28 polling CI was half the main thread's cost.
316
+
317
+ Fix each review round's findings in one batch. If a second round still finds
318
+ a problem in the same fix, take that fix out of the PR, remove its whole entry
319
+ from the PR's `Reports` section: the link and its `fix-proof:` and `visual:`
320
+ lines (close-out marks every listed report fixed, and a stray evidence line
321
+ counts toward the report above it). Then 🎫 its
322
+ report with what review found, and merge the rest. Close the PR only when nothing verified
323
+ is left in it.
324
+
296
325
  Merge it the way this repo merges PRs. In a Factory workspace, that means
297
326
  the workflow's merge step after its merge gate, never a direct merge.
298
327
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.222.2-nightly-20261029152548",
3
+ "version": "0.222.2",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -742,8 +742,8 @@
742
742
  "yaml": "^2.9.0",
743
743
  "yjs": "^13.6.33",
744
744
  "zod": "^4.3.6",
745
- "@agent-native/agentkit": "^0.222.2-nightly-20261029152548",
746
- "@agent-native/recap-cli": "^0.5.92"
745
+ "@agent-native/agentkit": "^0.222.2",
746
+ "@agent-native/recap-cli": "^0.5.93"
747
747
  },
748
748
  "devDependencies": {
749
749
  "@ai-sdk/anthropic": "^3.0.71",
@@ -814,7 +814,7 @@
814
814
  "xlsx": "^0.18.5"
815
815
  },
816
816
  "peerDependencies": {
817
- "@agent-native/core-corpus": "0.222.2-nightly-20261029152548",
817
+ "@agent-native/core-corpus": ">=0.196.0",
818
818
  "@ai-sdk/anthropic": ">=3",
819
819
  "@ai-sdk/cohere": ">=3",
820
820
  "@ai-sdk/google": ">=3",
@@ -163,9 +163,21 @@ checkout, or the expected worktree is missing, create one
163
163
  to stop.
164
164
 
165
165
  Window: roots posted in the last 5 days (or since the timestamp or permalink
166
- the invocation gives), for new reports and open items alike. Older threads
167
- are left as they are, except reports listed under `Reports` in a sweep PR
168
- opened or merged in that window: those always get their close-out.
166
+ the invocation gives), for new reports and open items alike. An older thread
167
+ is in the window too when we have already replied in it and a person has
168
+ replied since the window began: a channel read only shows roots, so search for
169
+ recent thread replies (`in:<#channel> is:thread after:<date>`) to find them.
170
+ On 10/29 a teammate's vote and question sat unanswered under a 3-week-old
171
+ root. Other older threads are left as they are, except reports listed under
172
+ `Reports` in a sweep PR opened or merged in that window: those always get
173
+ their close-out.
174
+
175
+ Reading a source is a good job for a helper: give each source below its own
176
+ helper on the cheapest model (Luna in Codex, Haiku in Claude Code), ask for a
177
+ short list of candidates with links, and close it as soon as it reports. On
178
+ 10/29 the three source reads cost $0.44 together; reusing those same helpers
179
+ for fixes afterwards cost $6.36, because each new job paid for its whole
180
+ growing history.
169
181
 
170
182
  - **Read the channels.** Use `slack_read_channel` with
171
183
  `response_format: detailed` (paginate) back 5 days. Its output shows
@@ -250,8 +262,9 @@ apart from Steve's own 👀.
250
262
  ## 3. Fix as many as you can
251
263
 
252
264
  - Group related reports so one cause gets one fix. Give each independent
253
- group a subagent on a cheaper model with disjoint files. The main thread
254
- plans, reviews diffs, and integrates.
265
+ group a fresh subagent on your own model and effort, with disjoint files,
266
+ or fix it in the main thread. Never hand a finished helper a new job; start
267
+ a fresh one. The main thread plans, reviews diffs, and integrates.
255
268
  - **Reproduce before you edit.** Get the failure on screen first: a test
256
269
  that fails, the action returning the wrong thing, a stack trace, or the
257
270
  flow broken in a signed-in local app. Reading code is not an attempt:
@@ -274,9 +287,12 @@ apart from Steve's own 👀.
274
287
  green, stop editing and capture the real failure first (stack, logs, the
275
288
  reporter's actual data). If that doesn't settle it within about 30
276
289
  minutes, it becomes 🎫 with one line on what you learned.
277
- - **Second opinion before the PR.** Give a fresh subagent only the report
278
- text, the diff, and the proof, and ask: does the check exercise what the
279
- reporter saw, and is this the cause or a symptom? Fix what it finds.
290
+ - **Second opinion before the PR, every time.** Give a fresh subagent on
291
+ your own model only the report text, the diff, and the proof, and ask: does
292
+ the check exercise what the reporter saw, is this the cause or a symptom,
293
+ and what edge case breaks it? Fix what it finds before you open the PR. The
294
+ 10/29 run skipped this, and PR review found the edge cases one round at a
295
+ time instead.
280
296
  - **Keep fixes small.** Each report's fix goes in its own commit so a bad
281
297
  one can be reverted alone. A fix that grows past about 300 lines, or into
282
298
  a new subsystem (timeouts, cancellation, retries) the reporter didn't ask
@@ -293,6 +309,19 @@ PR per item. End the PR body with a
293
309
  Slack thread, or the Sentry, Analytics, or GitHub issue. That list is the
294
310
  only set of threads a later `/ship` may close out as fixed.
295
311
 
312
+ Wait for CI with one blocking command, such as
313
+ `gh pr checks <number> --watch --required --interval 60`, and don't post a
314
+ status line while it runs: every turn re-sends your whole context, and on
315
+ 10/28 polling CI was half the main thread's cost.
316
+
317
+ Fix each review round's findings in one batch. If a second round still finds
318
+ a problem in the same fix, take that fix out of the PR, remove its whole entry
319
+ from the PR's `Reports` section: the link and its `fix-proof:` and `visual:`
320
+ lines (close-out marks every listed report fixed, and a stray evidence line
321
+ counts toward the report above it). Then 🎫 its
322
+ report with what review found, and merge the rest. Close the PR only when nothing verified
323
+ is left in it.
324
+
296
325
  Merge it the way this repo merges PRs. In a Factory workspace, that means
297
326
  the workflow's merge step after its merge gate, never a direct merge.
298
327