@agent-native/core 0.158.10 → 0.159.1

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.
Files changed (76) hide show
  1. package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +2 -2
  2. package/corpus/templates/clips/app/i18n/en-US.ts +7 -2
  3. package/corpus/templates/clips/app/routes/record.tsx +147 -5
  4. package/dist/a2a/auth-policy.d.ts +0 -6
  5. package/dist/a2a/auth-policy.js +2 -1
  6. package/dist/a2a/client.js +10 -12
  7. package/dist/agent/durable-background.js +3 -2
  8. package/dist/agent/engine/registry.js +5 -4
  9. package/dist/agent/run-manager.js +8 -20
  10. package/dist/app-config/a2a.d.ts +14 -0
  11. package/dist/app-config/a2a.js +42 -0
  12. package/dist/app-config/agent.d.ts +18 -0
  13. package/dist/app-config/agent.js +69 -0
  14. package/dist/app-config/app.d.ts +33 -0
  15. package/dist/app-config/app.js +100 -0
  16. package/dist/app-config/describe.d.ts +26 -0
  17. package/dist/app-config/describe.js +61 -0
  18. package/dist/app-config/env-layer.d.ts +37 -0
  19. package/dist/app-config/env-layer.js +136 -0
  20. package/dist/app-config/index.d.ts +2 -0
  21. package/dist/app-config/index.js +2 -0
  22. package/dist/app-config/integrations.d.ts +5 -0
  23. package/dist/app-config/integrations.js +11 -0
  24. package/dist/app-config/private-blob.d.ts +14 -0
  25. package/dist/app-config/private-blob.js +19 -0
  26. package/dist/app-config/schema.d.ts +55 -0
  27. package/dist/app-config/schema.js +31 -0
  28. package/dist/app-config/store.d.ts +42 -0
  29. package/dist/app-config/store.js +104 -0
  30. package/dist/app-config/workspace.d.ts +21 -0
  31. package/dist/app-config/workspace.js +35 -0
  32. package/dist/cli/agent.js +2 -1
  33. package/dist/cli/code-agent-executor.js +5 -4
  34. package/dist/client/analytics.d.ts +9 -0
  35. package/dist/client/frame.js +11 -1
  36. package/dist/deploy/build.js +36 -0
  37. package/dist/integrations/a2a-continuation-processor.js +12 -9
  38. package/dist/integrations/adapters/email.js +2 -1
  39. package/dist/integrations/adapters/telegram.js +2 -1
  40. package/dist/integrations/adapters/whatsapp.js +2 -1
  41. package/dist/integrations/webhook-handler.d.ts +7 -2
  42. package/dist/integrations/webhook-handler.js +9 -31
  43. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  44. package/dist/notifications/routes.d.ts +3 -3
  45. package/dist/observability/routes.d.ts +6 -6
  46. package/dist/onboarding/app-profile.js +2 -4
  47. package/dist/onboarding/default-steps.js +4 -4
  48. package/dist/private-blob/registry.d.ts +5 -0
  49. package/dist/private-blob/registry.js +19 -8
  50. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  51. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  52. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  53. package/dist/scripts/agent-engines/list-agent-engines.js +4 -2
  54. package/dist/secrets/routes.d.ts +3 -3
  55. package/dist/server/agent-chat/action-filters-a2a.js +5 -4
  56. package/dist/server/agent-chat/run-code-tools.js +3 -4
  57. package/dist/server/agent-chat-plugin.js +4 -3
  58. package/dist/server/agent-discovery.js +7 -5
  59. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  60. package/dist/server/app-origin-config.d.ts +23 -0
  61. package/dist/server/app-origin-config.js +43 -0
  62. package/dist/server/auth.js +2 -1
  63. package/dist/server/core-routes-plugin.js +3 -3
  64. package/dist/server/credential-provider.js +7 -8
  65. package/dist/server/embed-session.js +2 -1
  66. package/dist/server/identity-sso.js +6 -2
  67. package/dist/server/index.d.ts +1 -0
  68. package/dist/server/index.js +1 -0
  69. package/dist/server/oauth-public-origin.js +10 -14
  70. package/dist/server/oauth-return-url.js +5 -9
  71. package/dist/server/self-dispatch.js +5 -2
  72. package/dist/server/ssr-handler.js +2 -0
  73. package/dist/server/workspace-provider-oauth.js +2 -4
  74. package/dist/sharing/actions/create-agent-resource-link.js +2 -2
  75. package/dist/usage/metrics-store.js +2 -1
  76. package/package.json +1 -1
@@ -75,7 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- found?: undefined;
78
+ message?: undefined;
79
+ id?: undefined;
79
80
  deleted?: undefined;
80
81
  providers: {
81
82
  id: string;
@@ -88,46 +89,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
89
  }[];
89
90
  count: number;
90
91
  provider?: undefined;
92
+ found?: undefined;
91
93
  registered?: undefined;
92
- id?: undefined;
93
94
  label?: undefined;
94
- message?: undefined;
95
95
  } | {
96
+ message?: undefined;
97
+ count?: undefined;
98
+ id?: undefined;
96
99
  deleted?: undefined;
97
100
  providers?: undefined;
98
- count?: undefined;
99
101
  found: boolean;
100
102
  provider: import("../custom-registry.js").CustomProviderConfig;
101
103
  registered?: undefined;
102
- id?: undefined;
103
104
  label?: undefined;
104
- message?: undefined;
105
105
  } | {
106
+ message?: undefined;
107
+ count?: undefined;
106
108
  deleted?: undefined;
107
109
  providers?: undefined;
108
- count?: undefined;
109
110
  provider?: undefined;
110
111
  found: boolean;
111
112
  id: string;
112
113
  registered?: undefined;
113
114
  label?: undefined;
114
- message?: undefined;
115
115
  } | {
116
- found?: undefined;
117
- providers?: undefined;
116
+ message?: undefined;
118
117
  count?: undefined;
118
+ providers?: undefined;
119
119
  provider?: undefined;
120
+ found?: undefined;
120
121
  deleted: boolean;
121
122
  id: string;
122
123
  registered?: undefined;
123
124
  label?: undefined;
124
- message?: undefined;
125
125
  } | {
126
- found?: undefined;
126
+ count?: undefined;
127
127
  deleted?: undefined;
128
128
  providers?: undefined;
129
- count?: undefined;
130
129
  provider?: undefined;
130
+ found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,11 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
+ deleted?: undefined;
314
315
  id?: undefined;
315
316
  provider?: undefined;
316
- deleted?: undefined;
317
- found?: undefined;
318
- message?: undefined;
319
317
  providers: {
320
318
  id: string;
321
319
  label: string;
@@ -326,44 +324,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
326
324
  updatedAt: number;
327
325
  }[];
328
326
  count: number;
327
+ found?: undefined;
329
328
  registered?: undefined;
330
329
  label?: undefined;
330
+ message?: undefined;
331
331
  } | {
332
- id?: undefined;
333
332
  deleted?: undefined;
334
- message?: undefined;
333
+ id?: undefined;
335
334
  providers?: undefined;
336
335
  count?: undefined;
337
336
  found: boolean;
338
337
  provider: import("../custom-registry.js").CustomProviderConfig;
339
338
  registered?: undefined;
340
339
  label?: undefined;
340
+ message?: undefined;
341
341
  } | {
342
- provider?: undefined;
343
342
  deleted?: undefined;
344
- message?: undefined;
343
+ provider?: undefined;
345
344
  providers?: undefined;
346
345
  count?: undefined;
347
346
  found: boolean;
348
347
  id: string;
349
348
  registered?: undefined;
350
349
  label?: undefined;
350
+ message?: undefined;
351
351
  } | {
352
352
  provider?: undefined;
353
- found?: undefined;
354
- message?: undefined;
355
353
  providers?: undefined;
356
354
  count?: undefined;
355
+ found?: undefined;
357
356
  deleted: boolean;
358
357
  id: string;
359
358
  registered?: undefined;
360
359
  label?: undefined;
360
+ message?: undefined;
361
361
  } | {
362
- provider?: undefined;
363
362
  deleted?: undefined;
364
- found?: undefined;
363
+ provider?: undefined;
365
364
  providers?: undefined;
366
365
  count?: undefined;
366
+ found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
+ deleted?: undefined;
76
77
  jobs: {
77
78
  id: string;
78
79
  name: string;
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
83
84
  updatedAt: string;
84
85
  }[];
85
86
  total: number;
86
- deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
+ deleted?: undefined;
94
95
  jobs?: undefined;
95
96
  total?: undefined;
96
- deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -4,6 +4,7 @@
4
4
  import { getAgentAppModelDefaultForCurrentRequest } from "../../agent/app-model-defaults.js";
5
5
  import { DEFAULT_MODEL } from "../../agent/default-model.js";
6
6
  import { listAgentEngines, registerBuiltinEngines, detectEngineFromEnv, detectEngineFromUserSecrets, getAgentEngineEntry, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, normalizeModelForEngine, resolveEnginePreservesCustomModels, } from "../../agent/engine/index.js";
7
+ import { getAppConfig } from "../../app-config/index.js";
7
8
  import { getSetting } from "../../settings/index.js";
8
9
  export const tool = {
9
10
  description: 'List all available AI agent engines (Anthropic, OpenAI, Gemini, Groq, etc.) and the currently selected engine. Use this to check what engines are available before calling manage-agent-engine with action="set".',
@@ -37,8 +38,9 @@ export async function run(args = {}) {
37
38
  !!appDefaultEntry &&
38
39
  (await isStoredEngineUsableForRequest(appDefault, appDefaultEntry));
39
40
  const detectedFromUser = await detectEngineFromUserSecrets();
40
- const envEntry = process.env.AGENT_ENGINE
41
- ? getAgentEngineEntry(process.env.AGENT_ENGINE)
41
+ const configuredEngine = getAppConfig().agent.engine;
42
+ const envEntry = configuredEngine
43
+ ? getAgentEngineEntry(configuredEngine)
42
44
  : undefined;
43
45
  const envUsable = !!envEntry &&
44
46
  (await isStoredEngineUsableForRequest({ engine: envEntry.name }, envEntry));
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- ok?: undefined;
38
37
  status?: undefined;
38
+ ok?: undefined;
39
39
  } | {
40
40
  ok: boolean;
41
41
  status: string;
42
42
  error?: undefined;
43
43
  } | {
44
- ok?: undefined;
45
44
  error: string;
46
45
  removed?: undefined;
46
+ ok?: undefined;
47
47
  } | {
48
48
  ok: boolean;
49
49
  removed: boolean;
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
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;
58
57
  error: string;
59
58
  note?: undefined;
59
+ ok?: undefined;
60
60
  } | {
61
61
  ok: boolean;
62
62
  note?: undefined;
@@ -5,6 +5,7 @@ import { resolveMainChatMaxOutputTokens } from "../../agent/engine/output-tokens
5
5
  import { filterInitialEngineTools, resolveAgentRequestReasoningEffort, } from "../../agent/production-agent.js";
6
6
  import { runAgentLoopDirectWithSoftTimeout } from "../../agent/run-loop-with-resume.js";
7
7
  import { resolveRunSoftTimeoutMs } from "../../agent/run-manager.js";
8
+ import { getAppConfig } from "../../app-config/index.js";
8
9
  import { isFrameworkGroupedAction } from "../../framework-tools.js";
9
10
  import { isAuthenticatedReadAction, isAutoReadExcludedActionName, } from "../../mcp/build-server.js";
10
11
  import { withConfiguredAppBasePath } from "../app-base-path.js";
@@ -173,10 +174,10 @@ export function buildAuthenticatedAgentA2ASkills(actions, options) {
173
174
  });
174
175
  }
175
176
  export function resolveArtifactBaseUrl(event) {
176
- const fromEnv = process.env.APP_URL ||
177
- process.env.URL ||
178
- process.env.DEPLOY_URL ||
179
- process.env.BETTER_AUTH_URL;
177
+ // An artifact link is user-facing, so the canonical URL wins; the platform's
178
+ // per-deploy URLs are the fallback, not the other way round (that ordering
179
+ // belongs to self-dispatch, which has to reach *this* deploy).
180
+ const fromEnv = getAppConfig().app.url ?? process.env.URL ?? process.env.DEPLOY_URL;
180
181
  if (fromEnv)
181
182
  return withConfiguredAppBasePath(String(fromEnv));
182
183
  try {
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../../app-config/index.js";
1
2
  /**
2
3
  * Load the sandboxed code-execution tool entries for one action registry:
3
4
  * `run-code`, bounded `tool-orchestration`, plus its `get-code-execution` poll companion. The poll tool is
@@ -49,8 +50,6 @@ export async function loadRunCodeToolEntries(supplier, runCodeOptions) {
49
50
  * persisted resource scoped to "this app deployment".
50
51
  */
51
52
  function resolveDataProgramsAppId() {
52
- return (process.env.AGENT_NATIVE_APP_ID?.trim() ||
53
- process.env.APP_ID?.trim() ||
54
- process.env.APP_NAME?.trim() ||
55
- "app");
53
+ const app = getAppConfig().app;
54
+ return app.id ?? app.name ?? "app";
56
55
  }
@@ -20,6 +20,7 @@ import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agen
20
20
  import { buildAssistantMessage, buildUserMessage, claimQueuedMessage, extractThreadMeta, foldAssistantTurn, hasClaimedQueuedMessage, mergeThreadDataForClientSave, upsertUserMessage, } from "../agent/thread-data-builder.js";
21
21
  import { appendThreadDebugHistory } from "../agent/thread-debug-history.js";
22
22
  import { attachToolSearch } from "../agent/tool-search.js";
23
+ import { getAppConfig } from "../app-config/index.js";
23
24
  import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
24
25
  import { runChatThreadDataMigrations } from "../chat-threads/migrations.js";
25
26
  import { adoptThreadScopeIfUnscoped, createThread, forkThread, getThread, registerChatThreadsShareable, resolveRunThreadScope, resolveThreadAccess, listThreads, searchThreads, renameThread, createThreadShareLink, getThreadByShareToken, getThreadShareState, revokeThreadShareLink, setThreadArchived, setThreadPinned, setThreadScope, updateThreadData, withThreadDataLock, deleteThread, setThreadQueuedMessages, setThreadSourceIfMissing, } from "../chat-threads/store.js";
@@ -315,7 +316,7 @@ export function createAgentChatPlugin(options) {
315
316
  const hostedHarnessConfig = await loadHostedHarnessConfig();
316
317
  // AGENT_MODE=production forces production agent constraints even in dev
317
318
  const canToggle = (env === "development" || env === "test") &&
318
- process.env.AGENT_MODE !== "production";
319
+ getAppConfig().agent.mode !== "production";
319
320
  const routePath = options?.path ?? "/_agent-native/agent-chat";
320
321
  const a2aAgentDelegationEnabled = resolveA2AAgentDelegationEnabled(options);
321
322
  // Mutable mode flag — persisted to the `settings` table so a user who
@@ -3222,8 +3223,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
3222
3223
  }
3223
3224
  }));
3224
3225
  const modelDefaultsAppId = normalizeAgentAppModelDefaultAppId(options?.appId ??
3225
- process.env.AGENT_NATIVE_APP_ID ??
3226
- process.env.VITE_AGENT_NATIVE_TEMPLATE ??
3226
+ getAppConfig().app.id ??
3227
+ getAppConfig().app.template ??
3227
3228
  "app") ?? "app";
3228
3229
  const resolveModelDefaultsContext = async (event) => {
3229
3230
  const session = await getSession(event).catch(() => null);
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { getAppConfig } from "../app-config/index.js";
4
5
  import { TEMPLATES } from "../cli/templates-meta.js";
5
6
  import { DEFAULT_WORKSPACE_APP_AUDIENCE, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromPackageJson, } from "../shared/workspace-app-audience.js";
6
7
  import { getRequestOrgId, getRequestUserEmail } from "./request-context.js";
@@ -519,11 +520,12 @@ function readWorkspaceAppsFromFilesystem() {
519
520
  return apps.length ? apps : null;
520
521
  }
521
522
  function workspaceBaseUrl() {
522
- return (process.env.WORKSPACE_GATEWAY_URL ||
523
- process.env.APP_URL ||
524
- process.env.URL ||
525
- process.env.DEPLOY_URL ||
526
- process.env.BETTER_AUTH_URL ||
523
+ const config = getAppConfig();
524
+ // `URL` / `DEPLOY_URL` stay raw: they are platform facts, not app config.
525
+ return (config.workspace.gatewayUrl ??
526
+ config.app.url ??
527
+ process.env.URL ??
528
+ process.env.DEPLOY_URL ??
527
529
  null);
528
530
  }
529
531
  function workspaceAppUrl(app, hostedLoopbackFallbackUrl) {
@@ -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 {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Project this app's origins into the client shell.
3
+ *
4
+ * The browser needs these to build cross-frame and workspace-relative URLs.
5
+ * Historically that was done by mirroring each key with a `VITE_` twin, because
6
+ * Vite only inlines `VITE_`-prefixed variables into the bundle — so every
7
+ * consumer ended up writing both spellings, and half of each chain was dead on
8
+ * whichever side it happened to run.
9
+ *
10
+ * The mirror answered "how does this value reach the browser", never "what is
11
+ * this value". This is that answer instead: one declared field on the server,
12
+ * delivered through the same shell script Sentry and PostHog already use.
13
+ *
14
+ * Every value here is impersonal and identical for every visitor, which is what
15
+ * makes it safe inside the CDN-cached SSR shell (`guard:ssr-cache-shell`).
16
+ * Never add a per-user or per-request value to this object.
17
+ */
18
+ export declare function resolvePublicAppOriginConfig(): {
19
+ appUrl?: string;
20
+ workspaceGatewayUrl?: string;
21
+ workspaceOAuthOrigin?: string;
22
+ } | null;
23
+ export declare function getAppOriginClientConfigScript(): string | null;
@@ -0,0 +1,43 @@
1
+ import { getAppConfig } from "../app-config/index.js";
2
+ /**
3
+ * Project this app's origins into the client shell.
4
+ *
5
+ * The browser needs these to build cross-frame and workspace-relative URLs.
6
+ * Historically that was done by mirroring each key with a `VITE_` twin, because
7
+ * Vite only inlines `VITE_`-prefixed variables into the bundle — so every
8
+ * consumer ended up writing both spellings, and half of each chain was dead on
9
+ * whichever side it happened to run.
10
+ *
11
+ * The mirror answered "how does this value reach the browser", never "what is
12
+ * this value". This is that answer instead: one declared field on the server,
13
+ * delivered through the same shell script Sentry and PostHog already use.
14
+ *
15
+ * Every value here is impersonal and identical for every visitor, which is what
16
+ * makes it safe inside the CDN-cached SSR shell (`guard:ssr-cache-shell`).
17
+ * Never add a per-user or per-request value to this object.
18
+ */
19
+ export function resolvePublicAppOriginConfig() {
20
+ const config = getAppConfig();
21
+ const resolved = {
22
+ ...(config.app.url ? { appUrl: config.app.url } : {}),
23
+ ...(config.workspace.gatewayUrl
24
+ ? { workspaceGatewayUrl: config.workspace.gatewayUrl }
25
+ : {}),
26
+ ...(config.workspace.oauthOrigin
27
+ ? { workspaceOAuthOrigin: config.workspace.oauthOrigin }
28
+ : {}),
29
+ };
30
+ return Object.keys(resolved).length > 0 ? resolved : null;
31
+ }
32
+ export function getAppOriginClientConfigScript() {
33
+ const config = resolvePublicAppOriginConfig();
34
+ if (!config)
35
+ return null;
36
+ return [
37
+ "<script data-agent-native-app-origin-config>",
38
+ "window.__AGENT_NATIVE_CONFIG__=Object.assign({},window.__AGENT_NATIVE_CONFIG__,",
39
+ JSON.stringify(config),
40
+ ");",
41
+ "</script>",
42
+ ].join("");
43
+ }
@@ -1,5 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import { defineEventHandler, getMethod, getQuery, getRequestIP, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
3
+ import { getAppConfig } from "../app-config/index.js";
3
4
  import { EMBED_START_PATH } from "../shared/embed-auth.js";
4
5
  import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
5
6
  import { FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_MAX_AGE, } from "../shared/first-run-onboarding.js";
@@ -2708,7 +2709,7 @@ function isHttpsRequest(event) {
2708
2709
  const url = req?.url;
2709
2710
  if (typeof url === "string" && url.startsWith("https://"))
2710
2711
  return true;
2711
- const appUrl = process.env.APP_URL || process.env.BETTER_AUTH_URL || "";
2712
+ const appUrl = getAppConfig().app.url ?? "";
2712
2713
  if (appUrl.startsWith("https://"))
2713
2714
  return true;
2714
2715
  }
@@ -5,6 +5,7 @@ import { registerBuiltinEngines } from "../agent/engine/builtin.js";
5
5
  import { OPENAI_BASE_URL_ENV_VAR, PROVIDER_ENV_META, } from "../agent/engine/provider-env-vars.js";
6
6
  import { isAgentEngineSettingConfigured, getAgentEngineEntry, detectEngineFromEnv, detectEngineFromUserSecrets, isStoredEngineUsableForRequest, normalizeModelForEngine, } from "../agent/engine/registry.js";
7
7
  import { canUpdateAgentLoopSettings, readAgentLoopSettings, resetAgentLoopSettings, validateMaxIterationsInput, writeAgentLoopSettings, } from "../agent/loop-settings.js";
8
+ import { getAppConfig } from "../app-config/index.js";
8
9
  import { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, getStateMany, } from "../application-state/handlers.js";
9
10
  import { mountBrowserSessionRoutes } from "../browser-sessions/routes.js";
10
11
  import { mountDbAdminRoutes } from "../db-admin/routes.js";
@@ -102,9 +103,8 @@ export async function resolveAgentEngineStatus(deps) {
102
103
  openAiBaseUrlConfigured,
103
104
  };
104
105
  }
105
- const envEntry = process.env.AGENT_ENGINE
106
- ? lookupEntry(process.env.AGENT_ENGINE)
107
- : undefined;
106
+ const configuredEngine = getAppConfig().agent.engine;
107
+ const envEntry = configuredEngine ? lookupEntry(configuredEngine) : undefined;
108
108
  if (envEntry) {
109
109
  if (!(await deps.isStoredEngineUsable({ engine: envEntry.name }, envEntry)))
110
110
  return { configured: false, openAiBaseUrlConfigured };
@@ -19,6 +19,7 @@
19
19
  */
20
20
  import { createHash } from "node:crypto";
21
21
  import { CREDENTIAL_STORE_UNAVAILABLE_ERROR_CODE } from "../agent/engine/credential-errors.js";
22
+ import { getAppConfig } from "../app-config/index.js";
22
23
  import { getDbExec, isLocalDatabase, isTransientDatabaseError, } from "../db/client.js";
23
24
  import { getOrgSetting } from "../settings/org-settings.js";
24
25
  import { getRequestUserEmail, getRequestOrgId } from "./request-context.js";
@@ -35,14 +36,12 @@ async function canReadDesignatedVaultFallback(vaultOrgId, credentialKey) {
35
36
  // Manual mode is still usable across a Dispatch vault org and a separate
36
37
  // app org, but only for this app's explicit active grant. The app identity
37
38
  // comes from deployment configuration, never from request input.
38
- const appId = [
39
- process.env.AGENT_NATIVE_WORKSPACE_APP_ID,
40
- process.env.VITE_AGENT_NATIVE_WORKSPACE_APP_ID,
41
- process.env.AGENT_NATIVE_APP_ID,
42
- process.env.APP_NAME,
43
- ]
44
- .find((value) => value?.trim())
45
- ?.trim();
39
+ //
40
+ // `workspaceId` first is load-bearing: `vault_grants` rows are written with
41
+ // the id a workspace deploy assigns, so preferring the generic `id` would
42
+ // look grants up under a name nobody granted.
43
+ const app = getAppConfig().app;
44
+ const appId = app.workspaceId ?? app.id ?? app.name;
46
45
  if (!appId) {
47
46
  return { status: "denied", reason: "missing-app-id" };
48
47
  }
@@ -1,5 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import { getCookie, getHeader, getQuery, setCookie, setResponseHeader, } from "h3";
3
+ import { getAppConfig } from "../app-config/index.js";
3
4
  import { getDbExec, intType, isPostgres } from "../db/client.js";
4
5
  import { ensureTableExists } from "../db/ddl-guard.js";
5
6
  import { EMBED_MODE_QUERY_PARAM, EMBED_SESSION_COOKIE, EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, } from "../shared/embed-auth.js";
@@ -651,7 +652,7 @@ function isHttpsRequest(event) {
651
652
  const url = event.url?.toString?.() ?? "";
652
653
  if (url.startsWith("https://"))
653
654
  return true;
654
- const appUrl = process.env.APP_URL || process.env.BETTER_AUTH_URL || "";
655
+ const appUrl = getAppConfig().app.url ?? "";
655
656
  if (appUrl.startsWith("https://"))
656
657
  return true;
657
658
  }
@@ -15,6 +15,7 @@
15
15
  import { createHash, randomBytes } from "node:crypto";
16
16
  import { deleteCookie, getCookie, getHeader, getMethod, setCookie } from "h3";
17
17
  import * as jose from "jose";
18
+ import { getAppConfig } from "../app-config/index.js";
18
19
  import { GOOGLE_AUTH_REQUIRED_MESSAGE, isGoogleSignInRequiredForEmail, } from "../org/auth-policy.js";
19
20
  import { SIGN_IN_ENTRY_PATH } from "../shared/sign-in-journey.js";
20
21
  import { getAppName } from "./app-name.js";
@@ -98,8 +99,11 @@ function normalizeAuthority(raw) {
98
99
  }
99
100
  }
100
101
  function resolveAppId(event) {
101
- const configured = process.env.AGENT_NATIVE_APP_ID?.trim() ||
102
- process.env.AGENT_NATIVE_WORKSPACE_APP_ID?.trim();
102
+ // Generic id first here, unlike credential scoping: SSO identifies this app
103
+ // instance to an authority, it does not look up a row keyed by the id a
104
+ // workspace deploy assigned.
105
+ const app = getAppConfig().app;
106
+ const configured = app.id ?? app.workspaceId;
103
107
  if (configured)
104
108
  return configured;
105
109
  const name = getAppName();
@@ -1,3 +1,4 @@
1
+ export { defineAppConfig, getAppConfig, resetAppConfigForTests, appConfigSchema, type AppConfig, type AppConfigInput, } from "../app-config/index.js";
1
2
  export { createServer, type CreateServerOptions, type EnvKeyConfig, } from "./create-server.js";
2
3
  export { startIntervalJob, type IntervalJobOptions, type IntervalJobHandle, } from "./interval-job.js";
3
4
  export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, isInBackgroundFunctionRuntime, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
@@ -1,3 +1,4 @@
1
+ export { defineAppConfig, getAppConfig, resetAppConfigForTests, appConfigSchema, } from "../app-config/index.js";
1
2
  export { createServer, } from "./create-server.js";
2
3
  export { startIntervalJob, } from "./interval-job.js";
3
4
  export { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_ROUTE, AGENT_BACKGROUND_PROCESSOR_ROUTE_FIELD, dispatchPathTargetsNetlifyBackgroundFunction, isInBackgroundFunctionRuntime, resolveDurableBackgroundDispatchPath, } from "../agent/durable-background.js";
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../app-config/index.js";
1
2
  function normalizeOrigin(raw) {
2
3
  if (!raw)
3
4
  return "";
@@ -21,21 +22,16 @@ function isLoopbackOrigin(origin) {
21
22
  }
22
23
  }
23
24
  export function getPublicOAuthOrigin() {
25
+ const config = getAppConfig();
26
+ // An OAuth callback has to be publicly reachable, so a loopback origin is
27
+ // useless and we keep looking. This used to walk eight env keys — each
28
+ // canonical spelling followed by its `VITE_` mirror — so a loopback
29
+ // `APP_URL` could still fall through to a public `BETTER_AUTH_URL`. Those
30
+ // are one declared value now, so the skip is per concept, not per spelling.
24
31
  for (const raw of [
25
- process.env.WORKSPACE_OAUTH_ORIGIN,
26
- process.env.VITE_WORKSPACE_OAUTH_ORIGIN,
27
- process.env.APP_URL,
28
- process.env.VITE_APP_URL,
29
- process.env.BETTER_AUTH_URL,
30
- process.env.VITE_BETTER_AUTH_URL,
31
- ]) {
32
- const origin = normalizeOrigin(raw);
33
- if (origin && !isLoopbackOrigin(origin))
34
- return origin;
35
- }
36
- for (const raw of [
37
- process.env.WORKSPACE_GATEWAY_URL,
38
- process.env.VITE_WORKSPACE_GATEWAY_URL,
32
+ config.workspace.oauthOrigin,
33
+ config.app.url,
34
+ config.workspace.gatewayUrl,
39
35
  ]) {
40
36
  const origin = normalizeOrigin(raw);
41
37
  if (origin && !isLoopbackOrigin(origin))
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../app-config/index.js";
1
2
  function normalizeOrigin(raw) {
2
3
  if (!raw)
3
4
  return "";
@@ -39,15 +40,10 @@ function isBuilderPreviewOrigin(origin) {
39
40
  }
40
41
  }
41
42
  export function getWorkspaceGatewayReturnOrigin() {
42
- for (const raw of [
43
- process.env.WORKSPACE_GATEWAY_URL,
44
- process.env.VITE_WORKSPACE_GATEWAY_URL,
45
- ]) {
46
- const origin = normalizeOrigin(raw);
47
- if (origin && isLoopbackOrigin(origin))
48
- return origin;
49
- }
50
- return "";
43
+ // Loopback-only, inverted from getPublicOAuthOrigin: this is the local-dev
44
+ // gateway a return URL may point back at.
45
+ const origin = normalizeOrigin(getAppConfig().workspace.gatewayUrl);
46
+ return origin && isLoopbackOrigin(origin) ? origin : "";
51
47
  }
52
48
  function allowedOAuthReturnOrigins(allowDefaultLoopback) {
53
49
  const out = new Set();
@@ -1,3 +1,4 @@
1
+ import { getAppConfig } from "../app-config/index.js";
1
2
  import { isLocalDatabase } from "../db/client.js";
2
3
  import { signInternalToken } from "../integrations/internal-token.js";
3
4
  /**
@@ -57,11 +58,13 @@ function readHeader(event, name) {
57
58
  * fallback to a bad host there would drop background work invisibly.
58
59
  */
59
60
  export function resolveSelfDispatchBaseUrl(event) {
61
+ // The first three are platform facts — Netlify sets them per deploy, and
62
+ // they are what makes this resolve to *this* deployment rather than the
63
+ // canonical one. `app.url` is the last rung, not the first, for that reason.
60
64
  const fromEnv = process.env.DEPLOY_PRIME_URL ||
61
65
  process.env.DEPLOY_URL ||
62
66
  process.env.URL ||
63
- process.env.APP_URL ||
64
- process.env.BETTER_AUTH_URL;
67
+ getAppConfig().app.url;
65
68
  if (fromEnv)
66
69
  return withConfiguredAppBasePath(String(fromEnv));
67
70
  if (process.env.NODE_ENV === "production" || !isLocalDatabase()) {
@@ -21,6 +21,7 @@ import { isMcpPublicPath } from "../mcp/route-paths.js";
21
21
  import { DEFAULT_SPECULATION_RULES_PATH, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, } from "../shared/cache-control.js";
22
22
  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";
23
23
  import { getAppBasePathFromViteEnv, stripAppBasePath as canonicalStripAppBasePath, } from "./app-base-path.js";
24
+ import { getAppOriginClientConfigScript } from "./app-origin-config.js";
24
25
  import { captureError } from "./capture-error.js";
25
26
  import { getPostHogClientConfigScript } from "./posthog-config.js";
26
27
  import { runWithRequestContext } from "./request-context.js";
@@ -307,6 +308,7 @@ async function rewriteMountedResponse(response, basePath, pathname, requestUrl)
307
308
  getSentryClientConfigScript(),
308
309
  getPostHogClientConfigScript(),
309
310
  getRealtimeClientConfigScript(),
311
+ getAppOriginClientConfigScript(),
310
312
  ]
311
313
  .filter(Boolean)
312
314
  .join("") || null;
@@ -1,5 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import { deleteCookie, defineEventHandler, getCookie, getMethod, getQuery, setCookie, setResponseStatus, } from "h3";
3
+ import { getAppConfig } from "../app-config/index.js";
3
4
  import { getWorkspaceConnectionProvider, } from "../connections/catalog.js";
4
5
  import { saveOAuthTokens, setOAuthDisplayName } from "../oauth-tokens/store.js";
5
6
  import { getRegisteredAppRoles, resolveAppRole } from "../org/app-roles.js";
@@ -44,10 +45,7 @@ export async function handleWorkspaceProviderOAuthStart(event, providerId) {
44
45
  if (!session?.email)
45
46
  return unauthorized(event);
46
47
  const query = getQuery(event);
47
- const appId = normalizeAppId(text(query.appId) ??
48
- process.env.AGENT_NATIVE_WORKSPACE_APP_ID ??
49
- process.env.VITE_AGENT_NATIVE_WORKSPACE_APP_ID ??
50
- "creative-context");
48
+ const appId = normalizeAppId(text(query.appId) ?? getAppConfig().app.workspaceId ?? "creative-context");
51
49
  const orgContext = await requireWorkspaceProviderOAuthAdmin(event, appId);
52
50
  if (!orgContext) {
53
51
  return { error: WORKSPACE_OAUTH_ADMIN_ERROR };