@agent-native/core 0.168.1 → 0.168.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.
Files changed (50) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/netlify.toml +4 -4
  3. package/corpus/templates/assets/netlify.toml +4 -1
  4. package/corpus/templates/brain/netlify.toml +4 -1
  5. package/corpus/templates/calendar/netlify.toml +4 -1
  6. package/corpus/templates/chat/netlify.toml +4 -1
  7. package/corpus/templates/clips/netlify.toml +4 -1
  8. package/corpus/templates/content/netlify.toml +4 -1
  9. package/corpus/templates/crm/netlify.toml +6 -1
  10. package/corpus/templates/design/netlify.toml +4 -1
  11. package/corpus/templates/dispatch/netlify.toml +4 -1
  12. package/corpus/templates/factory/netlify.toml +4 -1
  13. package/corpus/templates/forms/netlify.toml +4 -1
  14. package/corpus/templates/macros/netlify.toml +3 -3
  15. package/corpus/templates/mail/actions/view-screen.ts +80 -8
  16. package/corpus/templates/mail/app/components/email/EmailList.tsx +3 -1
  17. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +1 -35
  18. package/corpus/templates/mail/app/components/email/EmailThread.tsx +16 -8
  19. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +146 -87
  20. package/corpus/templates/mail/app/global.css +21 -0
  21. package/corpus/templates/mail/app/hooks/use-emails.ts +6 -0
  22. package/corpus/templates/mail/app/hooks/use-navigation-state.ts +2 -0
  23. package/corpus/templates/mail/app/lib/inbox-tabs.ts +5 -0
  24. package/corpus/templates/mail/app/lib/label-colors.ts +47 -0
  25. package/corpus/templates/mail/app/pages/InboxPage.tsx +25 -8
  26. package/corpus/templates/mail/netlify.toml +4 -1
  27. package/corpus/templates/mail/server/handlers/emails.ts +27 -2
  28. package/corpus/templates/plan/netlify.toml +4 -1
  29. package/corpus/templates/slides/netlify.toml +4 -1
  30. package/corpus/templates/tasks/netlify.toml +6 -1
  31. package/dist/app-config/migration.d.ts +5 -0
  32. package/dist/app-config/migration.js +11 -0
  33. package/dist/app-config/schema.d.ts +4 -0
  34. package/dist/app-config/schema.js +2 -0
  35. package/dist/collab/awareness.d.ts +2 -2
  36. package/dist/db/migrations.js +10 -2
  37. package/dist/deploy/build.js +6 -0
  38. package/dist/deploy/netlify-static-headers.d.ts +3 -0
  39. package/dist/deploy/netlify-static-headers.js +60 -0
  40. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  41. package/dist/notifications/routes.d.ts +3 -3
  42. package/dist/observability/routes.d.ts +3 -3
  43. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  44. package/dist/provider-api/actions/provider-api.d.ts +17 -17
  45. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  46. package/dist/server/transcribe-voice.d.ts +1 -1
  47. package/dist/templates/chat/netlify.toml +4 -1
  48. package/dist/vite/client.js +2 -0
  49. package/package.json +2 -1
  50. package/src/templates/chat/netlify.toml +4 -1
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: 8491
34
+ - template files: 8492
@@ -4,7 +4,7 @@ ignore = "node ./scripts/netlify-ignore-build.mjs analytics"
4
4
  # core explicitly here so a cached Netlify install cannot bundle an older agent
5
5
  # loop into Analytics' server functions (including A2A/MCP final-response
6
6
  # guards).
7
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && pnpm --filter @agent-native/core build && NITRO_PRESET=netlify pnpm --filter analytics build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter analytics migrate:production; fi"
7
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && pnpm --filter @agent-native/core build && NITRO_PRESET=netlify pnpm --filter analytics build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter analytics migrate:production; fi"
8
8
  publish = "templates/analytics/dist"
9
9
  functions = "templates/analytics/.netlify/functions-internal"
10
10
 
@@ -27,9 +27,9 @@ AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
27
27
  # its loading screen while those chunks return 404.
28
28
  AGENT_NATIVE_SSR_CACHE = "30s"
29
29
  # Analytics owns its schema at release time (`migrate:production` above), so
30
- # production request functions skip migrations. Preview functions still run
31
- # request-path migrations because previews do not run the production release
32
- # migration step.
30
+ # production request functions skip migrations. Ordinary deploy previews still
31
+ # run request-path migrations; the beta prebuilt lane explicitly supplies the
32
+ # release owner even though it builds with branch-deploy context.
33
33
  AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
34
34
 
35
35
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs assets"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter assets build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter assets migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter assets build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter assets migrate:production; fi"
4
4
  publish = "templates/assets/dist"
5
5
  functions = "templates/assets/.netlify/functions-internal"
6
6
 
@@ -15,6 +15,9 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
15
15
  AGENT_NATIVE_HOSTED_HARNESS = "true"
16
16
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
17
17
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
18
+ # Production release migrations own schema setup; request functions must skip
19
+ # migration probes on every serverless invocation.
20
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
18
21
 
19
22
  # Emit the Netlify background function used by Assets' durable agent-chat opt-in.
20
23
  # The normal server timeout still protects non-agent image/video HTTP handlers.
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs brain"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter brain build && pnpm exec tsx templates/brain/jobs/emit-netlify-brain-queue-cron.ts && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter brain migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter brain build && pnpm exec tsx templates/brain/jobs/emit-netlify-brain-queue-cron.ts && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter brain migrate:production; fi"
4
4
  publish = "templates/brain/dist"
5
5
  functions = "templates/brain/.netlify/functions-internal"
6
6
 
@@ -14,6 +14,9 @@ NPM_CONFIG_PRODUCTION = "false"
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
15
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
16
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
17
+ # Production release migrations own schema setup; request functions must skip
18
+ # migration probes on every serverless invocation.
19
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
17
20
 
18
21
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
19
22
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs calendar"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter calendar build && if [ \"${CONTEXT:-}\" = \"production\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter calendar migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter calendar build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter calendar migrate:production; fi"
4
4
  publish = "templates/calendar/dist"
5
5
  functions = "templates/calendar/.netlify/functions-internal"
6
6
 
@@ -14,6 +14,9 @@ NPM_CONFIG_PRODUCTION = "false"
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
15
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
16
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
17
+ # Production release migrations own schema setup; request functions must skip
18
+ # migration probes on every serverless invocation.
19
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
17
20
 
18
21
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
19
22
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs chat"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter chat build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter chat migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter chat build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter chat migrate:production; fi"
4
4
  publish = "templates/chat/dist"
5
5
  functions = "templates/chat/.netlify/functions-internal"
6
6
 
@@ -14,3 +14,6 @@ NPM_CONFIG_PRODUCTION = "false"
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
15
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
16
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
17
+ # Production release migrations own schema setup; request functions must skip
18
+ # migration probes on every serverless invocation.
19
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs clips"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && if [ \"${agentNativePrebuiltBuild:-}\" = \"true\" ]; then export DATABASE_URL=\"${agentNativePrebuiltDatabaseUrl:?}\" BETTER_AUTH_SECRET=\"${agentNativePrebuiltAuthSecret:?}\"; fi && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build && pnpm exec tsx templates/clips/jobs/emit-netlify-brain-export-cron.ts && if [ \"${CONTEXT:-}\" = \"production\" ] && [ \"${agentNativePrebuiltBuild:-}\" != \"true\" ]; then pnpm --filter clips migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && if [ \"${agentNativePrebuiltBuild:-}\" = \"true\" ]; then export DATABASE_URL=\"${agentNativePrebuiltDatabaseUrl:?}\" BETTER_AUTH_SECRET=\"${agentNativePrebuiltAuthSecret:?}\"; fi && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build && pnpm exec tsx templates/clips/jobs/emit-netlify-brain-export-cron.ts && if { [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; } && [ \"${agentNativePrebuiltBuild:-}\" != \"true\" ]; then pnpm --filter clips migrate:production; fi"
4
4
  publish = "templates/clips/dist"
5
5
  functions = "templates/clips/.netlify/functions-internal"
6
6
 
@@ -18,6 +18,9 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
18
18
  AGENT_NATIVE_HOSTED_HARNESS = "true"
19
19
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
20
20
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
21
+ # Production release migrations own schema setup; request functions must skip
22
+ # migration probes on every serverless invocation.
23
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
21
24
 
22
25
  # Netlify Functions run on linux/x64 (AWS Lambda). This tells the deploy build
23
26
  # to bundle the matching ffmpeg-static binary into the server bundle so frame
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs content"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter content build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter content migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter content build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter content migrate:production; fi"
4
4
  publish = "templates/content/dist"
5
5
  functions = "templates/content/.netlify/functions-internal"
6
6
 
@@ -18,6 +18,9 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
18
18
  AGENT_NATIVE_HOSTED_HARNESS = "true"
19
19
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
20
20
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
21
+ # Production release migrations own schema setup; request functions must skip
22
+ # migration probes on every serverless invocation.
23
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
21
24
 
22
25
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
23
26
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs crm"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter crm build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter crm migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter crm build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter crm migrate:production; fi"
4
4
  publish = "templates/crm/dist"
5
5
  functions = "templates/crm/.netlify/functions-internal"
6
6
 
@@ -9,3 +9,8 @@ GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
9
  GTM_CONTAINER_ID = "GTM-N3WSTXZ"
10
10
  NITRO_PRESET = "netlify"
11
11
  NPM_CONFIG_PRODUCTION = "false"
12
+
13
+ [context.production.environment]
14
+ # Production release migrations own schema setup; request functions must skip
15
+ # migration probes on every serverless invocation.
16
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs design"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter design build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter design migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter design build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter design migrate:production; fi"
4
4
  publish = "templates/design/dist"
5
5
  functions = "templates/design/.netlify/functions-internal"
6
6
 
@@ -15,6 +15,9 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
15
15
  AGENT_NATIVE_HOSTED_HARNESS = "true"
16
16
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
17
17
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
18
+ # Production release migrations own schema setup; request functions must skip
19
+ # migration probes on every serverless invocation.
20
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
18
21
 
19
22
  # Emit the Netlify background function used by Design's durable agent-chat opt-in.
20
23
  # The normal server timeout still protects non-background HTTP handlers.
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs dispatch"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter dispatch build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter dispatch migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter dispatch build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then pnpm --filter dispatch migrate:production; fi"
4
4
  publish = "templates/dispatch/dist"
5
5
  functions = "templates/dispatch/.netlify/functions-internal"
6
6
 
@@ -17,6 +17,9 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
17
17
  AGENT_NATIVE_HOSTED_HARNESS = "true"
18
18
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
19
19
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
20
+ # Production release migrations own schema setup; request functions must skip
21
+ # migration probes on every serverless invocation.
22
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
20
23
 
21
24
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
22
25
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,5 +1,5 @@
1
1
  [build]
2
- command = "export DATABASE_URL=\"${NETLIFY_DATABASE_URL:-$DATABASE_URL}\" && pnpm install && DATABASE_URL=\"${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL}\" NITRO_PRESET=netlify pnpm --filter factory build && if [ \"${CONTEXT:-}\" = \"production\" ]; then DATABASE_URL=\"${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL}\" pnpm --filter factory migrate:production; fi"
2
+ command = "export DATABASE_URL=\"${NETLIFY_DATABASE_URL:-$DATABASE_URL}\" && pnpm install && DATABASE_URL=\"${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL}\" NITRO_PRESET=netlify pnpm --filter factory build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then DATABASE_URL=\"${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL}\" pnpm --filter factory migrate:production; fi"
3
3
  publish = "dist"
4
4
  functions = ".netlify/functions-internal"
5
5
 
@@ -11,3 +11,6 @@ NPM_CONFIG_PRODUCTION = "false"
11
11
 
12
12
  [context.production.environment]
13
13
  AGENT_NATIVE_HOSTED_HARNESS = "true"
14
+ # Production release migrations own schema setup; request functions must skip
15
+ # migration probes on every serverless invocation.
16
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs forms"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter forms build && if [ \"${CONTEXT:-}\" = \"production\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter forms migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter forms build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter forms migrate:production; fi"
4
4
  publish = "templates/forms/dist"
5
5
  functions = "templates/forms/.netlify/functions-internal"
6
6
 
@@ -14,6 +14,9 @@ NPM_CONFIG_PRODUCTION = "false"
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
15
  AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
16
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
17
+ # Production release migrations own schema setup; request functions must skip
18
+ # migration probes on every serverless invocation.
19
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
17
20
 
18
21
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
19
22
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs macros"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter macros build && if [ \"${CONTEXT:-}\" = \"production\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter macros migrate:production; fi"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} NITRO_PRESET=netlify pnpm --filter macros build && if [ \"${CONTEXT:-}\" = \"production\" ] || [ \"${AGENT_NATIVE_RUN_RELEASE_MIGRATIONS:-}\" = \"1\" ]; then DATABASE_URL=${NETLIFY_DATABASE_URL_UNPOOLED:-$DATABASE_URL} pnpm --filter macros migrate:production; fi"
4
4
  publish = "templates/macros/dist"
5
5
  functions = "templates/macros/.netlify/functions-internal"
6
6
 
@@ -16,6 +16,6 @@ AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
16
  AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
17
17
  # Macros owns its schema at release time (`migrate:production` above), so
18
18
  # production request functions must not probe the database for migrations.
19
- # Preview functions still run request-path migrations because previews do not
20
- # run the production release migration step.
19
+ # Ordinary deploy previews still run request-path migrations; the beta
20
+ # prebuilt lane explicitly supplies the release owner.
21
21
  AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
@@ -5,6 +5,12 @@ import { getSetting } from "@agent-native/core/settings";
5
5
  import { emailMessageMatchesSearch } from "@shared/search.js";
6
6
  import { z } from "zod";
7
7
 
8
+ import {
9
+ augmentSelfSentLabels,
10
+ filterInboxTabEmails,
11
+ OTHER_INBOX_TAB_PARAM,
12
+ resolvePinnedLabels,
13
+ } from "../app/lib/inbox-tabs.js";
8
14
  import { buildGmailEmailSearchQuery } from "../server/lib/gmail-query.js";
9
15
  import { gmailGetThread } from "../server/lib/google-api.js";
10
16
  import {
@@ -16,10 +22,12 @@ import {
16
22
  fetchGmailLabelMap,
17
23
  } from "../server/lib/google-auth.js";
18
24
  import { getSyntheticEmailsForView } from "../server/lib/jobs.js";
25
+ import { readSettings } from "../server/lib/mail-settings.js";
19
26
  import {
20
27
  listQueuedDrafts,
21
28
  requireQueuedDraft,
22
29
  } from "../server/lib/queued-drafts.js";
30
+ import type { EmailMessage } from "../shared/types.js";
23
31
  import { getAccessTokens, fetchLabelMap } from "./helpers.js";
24
32
 
25
33
  function latestPerThread(emails: any[]): any[] {
@@ -43,10 +51,33 @@ async function fetchEmailList(
43
51
  view: string,
44
52
  search?: string,
45
53
  _label?: string,
54
+ activeInboxTab?: string,
55
+ activeAccounts?: string[],
46
56
  ): Promise<any[]> {
47
57
  try {
48
58
  const ownerEmail = getRequestUserEmail();
49
59
  if (!ownerEmail) throw new Error("no authenticated user");
60
+ const shouldFilterOther =
61
+ view === "inbox" && !search && activeInboxTab === OTHER_INBOX_TAB_PARAM;
62
+ const selectedAccountEmails = Array.isArray(activeAccounts)
63
+ ? [
64
+ ...new Set(
65
+ activeAccounts
66
+ .filter((email): email is string => typeof email === "string")
67
+ .map((email) => email.toLowerCase()),
68
+ ),
69
+ ]
70
+ : [];
71
+ const selectedAccountSet = new Set(selectedAccountEmails);
72
+ const filterSelectedAccounts = (emails: any[]) => {
73
+ if (selectedAccountEmails.length === 0) return emails;
74
+ return emails.filter(
75
+ (email) =>
76
+ typeof email.accountEmail === "string" &&
77
+ selectedAccountSet.has(email.accountEmail.toLowerCase()),
78
+ );
79
+ };
80
+
50
81
  if (view === "snoozed" || view === "scheduled") {
51
82
  let emails = await getSyntheticEmailsForView(ownerEmail, view);
52
83
  if (search) {
@@ -54,10 +85,37 @@ async function fetchEmailList(
54
85
  emailMessageMatchesSearch(e, search),
55
86
  );
56
87
  }
57
- return emails.slice(0, 50);
88
+ return filterSelectedAccounts(emails).slice(0, 50);
58
89
  }
59
- if (await isConnected(ownerEmail)) {
60
- const clients = await getClients(ownerEmail);
90
+
91
+ const googleConnected = await isConnected(ownerEmail);
92
+ const settings =
93
+ googleConnected || shouldFilterOther
94
+ ? await readSettings(ownerEmail)
95
+ : undefined;
96
+ const userPinnedLabels = settings?.pinnedLabels;
97
+ const pinnedLabels =
98
+ userPinnedLabels === undefined
99
+ ? resolvePinnedLabels([], googleConnected)
100
+ : resolvePinnedLabels(userPinnedLabels, googleConnected);
101
+ const hasNoteToSelf = pinnedLabels.includes("note-to-self");
102
+ const clients = googleConnected ? await getClients(ownerEmail) : [];
103
+ const connectedEmails = new Set(
104
+ clients.map(({ email }) => email.toLowerCase()),
105
+ );
106
+ const prepareEmails = (emails: any[]) => {
107
+ const augmented = augmentSelfSentLabels(emails as EmailMessage[], {
108
+ isGoogleConnected: googleConnected,
109
+ connectedEmails,
110
+ hasNoteToSelf,
111
+ });
112
+ return filterSelectedAccounts(augmented);
113
+ };
114
+ const applyActiveInboxTab = (emails: any[]) =>
115
+ shouldFilterOther
116
+ ? filterInboxTabEmails(prepareEmails(emails), null, pinnedLabels)
117
+ : prepareEmails(emails);
118
+ if (googleConnected) {
61
119
  const labelMap = new Map<string, string>();
62
120
  await Promise.all(
63
121
  clients.map(async ({ accessToken }) => {
@@ -79,6 +137,10 @@ async function fetchEmailList(
79
137
  {
80
138
  mode: "threads",
81
139
  threadFormat: "metadata",
140
+ accountEmails:
141
+ selectedAccountEmails.length > 0
142
+ ? selectedAccountEmails
143
+ : undefined,
82
144
  threadCandidateLimit: search ? 500 : undefined,
83
145
  threadRecentMessageCandidateLimit:
84
146
  !search && (view === "inbox" || view === "unread")
@@ -87,9 +149,11 @@ async function fetchEmailList(
87
149
  },
88
150
  );
89
151
 
90
- return latestPerThread(
91
- messages.map((m: any) =>
92
- gmailToEmailMessage(m, m._accountEmail, labelMap),
152
+ return applyActiveInboxTab(
153
+ latestPerThread(
154
+ messages.map((m: any) =>
155
+ gmailToEmailMessage(m, m._accountEmail, labelMap),
156
+ ),
93
157
  ),
94
158
  ).slice(0, 50);
95
159
  }
@@ -136,7 +200,7 @@ async function fetchEmailList(
136
200
  emailMessageMatchesSearch(e, search),
137
201
  );
138
202
  }
139
- return emails.slice(0, 50);
203
+ return applyActiveInboxTab(emails).slice(0, 50);
140
204
  }
141
205
  return [];
142
206
  } catch {
@@ -255,7 +319,13 @@ export default defineAction({
255
319
  };
256
320
  }
257
321
  } else if (nav?.view) {
258
- const emails = await fetchEmailList(nav.view, nav.search, nav.label);
322
+ const emails = await fetchEmailList(
323
+ nav.view,
324
+ nav.search,
325
+ nav.label,
326
+ nav.activeInboxTab,
327
+ nav.activeAccounts,
328
+ );
259
329
  const selectedThreadIds = Array.isArray(nav.selectedThreadIds)
260
330
  ? new Set(
261
331
  nav.selectedThreadIds.filter(
@@ -279,6 +349,8 @@ export default defineAction({
279
349
  screen.emailList = {
280
350
  view: nav.view,
281
351
  label: nav.label ?? null,
352
+ activeInboxTab: nav.activeInboxTab ?? null,
353
+ activeAccounts: nav.activeAccounts ?? [],
282
354
  search: nav.search ?? null,
283
355
  selectedThreadIds: Array.from(selectedThreadIds),
284
356
  count: compact.length,
@@ -48,6 +48,7 @@ import {
48
48
  useBulkToggleStar,
49
49
  useBulkMarkRead,
50
50
  useLabels,
51
+ EMPTY_LABELS,
51
52
  useMoveEmail,
52
53
  unsuppressThread,
53
54
  } from "@/hooks/use-emails";
@@ -435,7 +436,8 @@ export function EmailList({
435
436
  const bulkTrashEmails = useBulkTrashEmails();
436
437
  const bulkToggleStar = useBulkToggleStar();
437
438
  const bulkMarkRead = useBulkMarkRead();
438
- const { data: labels = [] } = useLabels();
439
+ const { data: labelsData } = useLabels();
440
+ const labels = labelsData ?? EMPTY_LABELS;
439
441
  const moveEmail = useMoveEmail();
440
442
  const cancelScheduledJob = useDeleteScheduledJob();
441
443
  const sendScheduledJobNow = useSendScheduledJobNow();
@@ -21,6 +21,7 @@ import {
21
21
  TooltipTrigger,
22
22
  } from "@/components/ui/tooltip";
23
23
  import { useAccountFilter } from "@/hooks/use-account-filter";
24
+ import { getLabelStyle } from "@/lib/label-colors";
24
25
  import type { ThreadSummary } from "@/lib/threads";
25
26
  import { cn, formatEmailDate, truncate } from "@/lib/utils";
26
27
 
@@ -96,29 +97,6 @@ function formatParticipants(participants: string[], maxWidth = 3): string {
96
97
  return `${firstNames[0]} .. ${firstNames.slice(-(maxWidth - 1)).join(", ")}`;
97
98
  }
98
99
 
99
- // Map common label IDs to display colors
100
- const labelColors: Record<string, { bg: string; text: string }> = {
101
- automated: { bg: "bg-pink-500/20", text: "text-pink-700 dark:text-pink-300" },
102
- social: { bg: "bg-blue-500/20", text: "text-blue-700 dark:text-blue-300" },
103
- updates: {
104
- bg: "bg-yellow-500/20",
105
- text: "text-yellow-700 dark:text-yellow-300",
106
- },
107
- promotions: {
108
- bg: "bg-green-500/20",
109
- text: "text-green-700 dark:text-green-300",
110
- },
111
- forums: {
112
- bg: "bg-sky-500/20",
113
- text: "text-sky-700 dark:text-sky-300",
114
- },
115
- finance: {
116
- bg: "bg-emerald-500/20",
117
- text: "text-emerald-700 dark:text-emerald-300",
118
- },
119
- travel: { bg: "bg-cyan-500/20", text: "text-cyan-700 dark:text-cyan-300" },
120
- };
121
-
122
100
  /** Stable dot colors for distinguishing accounts */
123
101
  const accountDotColors = [
124
102
  "bg-blue-400",
@@ -139,18 +117,6 @@ function getAccountColor(
139
117
  return accountDotColors[(idx >= 0 ? idx : 0) % accountDotColors.length];
140
118
  }
141
119
 
142
- function getLabelStyle(labelId: string): { bg: string; text: string } {
143
- const normalized = labelId.toLowerCase().replace(/^label:/, "");
144
- if (labelColors[normalized]) return labelColors[normalized];
145
- // Fallback: hash to a color
146
- let hash = 0;
147
- for (let i = 0; i < normalized.length; i++) {
148
- hash = normalized.charCodeAt(i) + ((hash << 5) - hash);
149
- }
150
- const options = Object.values(labelColors);
151
- return options[Math.abs(hash) % options.length];
152
- }
153
-
154
120
  export const EmailListItem = memo(function EmailListItem({
155
121
  email,
156
122
  thread,
@@ -64,6 +64,7 @@ import {
64
64
  decodeHtmlEntities,
65
65
  processHtmlImages,
66
66
  } from "@/lib/email-image-policy";
67
+ import { getLabelStyle } from "@/lib/label-colors";
67
68
  import { isMcpEmbedSurface } from "@/lib/mcp-embed";
68
69
  import { getResolvedTheme } from "@/lib/theme";
69
70
  import { ensureThread, warmThreads } from "@/lib/thread-cache";
@@ -1138,14 +1139,21 @@ export function EmailThread({
1138
1139
  <h1 className="text-base sm:text-lg font-semibold leading-tight text-foreground line-clamp-2">
1139
1140
  {threadSubject}
1140
1141
  </h1>
1141
- {displayLabels.map((labelId) => (
1142
- <span
1143
- key={labelId}
1144
- className="label-badge shrink-0 bg-pink-500/20 text-pink-700 dark:text-pink-300 mt-1"
1145
- >
1146
- {labelId}
1147
- </span>
1148
- ))}
1142
+ {displayLabels.map((labelId) => {
1143
+ const style = getLabelStyle(labelId);
1144
+ return (
1145
+ <span
1146
+ key={labelId}
1147
+ className={cn(
1148
+ "label-badge shrink-0 mt-1",
1149
+ style.bg,
1150
+ style.text,
1151
+ )}
1152
+ >
1153
+ {labelId}
1154
+ </span>
1155
+ );
1156
+ })}
1149
1157
  {/* Action bar */}
1150
1158
  <div className="hidden sm:flex items-center gap-0.5 ml-auto shrink-0">
1151
1159
  <Tooltip>