@agent-native/core 0.146.8 → 0.147.0

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 (169) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -6
  3. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  4. package/corpus/templates/analytics/app/routes/status.$slug.tsx +4 -2
  5. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +56 -6
  6. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -6
  7. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  8. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +9 -0
  9. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -6
  10. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  11. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -6
  12. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  13. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  14. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  15. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  16. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -6
  17. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  18. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +16 -4
  19. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +14 -5
  20. package/corpus/templates/clips/package.json +1 -0
  21. package/corpus/templates/clips/react-router.config.ts +7 -0
  22. package/corpus/templates/clips/server/plugins/auth.ts +6 -1
  23. package/corpus/templates/clips/shared/prerendered-public-paths.ts +21 -0
  24. package/corpus/templates/clips/shared/share-meta.ts +8 -3
  25. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -6
  26. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  27. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -6
  28. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  29. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -6
  30. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  31. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -6
  32. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  33. package/corpus/templates/dispatch/app/chat-first-pages.ts +747 -0
  34. package/corpus/templates/dispatch/app/i18n-data.ts +181 -0
  35. package/corpus/templates/dispatch/app/routes/settings.tsx +62 -1
  36. package/corpus/templates/dispatch/changelog/2026-08-08-chat-first-navigation-keeps-conversations-central-while-open.md +6 -0
  37. package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +9 -0
  38. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -6
  39. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  40. package/corpus/templates/factory/app/routes/factory.tsx +108 -76
  41. package/corpus/templates/factory/changelog/2026-08-08-scheduler-health-moves-to-settings.md +6 -0
  42. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -6
  43. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  44. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -6
  45. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  46. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -6
  47. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  48. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +9 -0
  49. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -6
  50. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  51. package/corpus/templates/plan/app/lib/plan-document-title.ts +3 -1
  52. package/corpus/templates/plan/app/root.tsx +1 -0
  53. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -6
  54. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  55. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +9 -0
  56. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -6
  57. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  58. package/dist/agent/engine/builder-engine.d.ts +11 -1
  59. package/dist/agent/engine/builder-engine.js +11 -4
  60. package/dist/chat-threads/store.d.ts +2 -0
  61. package/dist/chat-threads/store.js +1 -1
  62. package/dist/cli/agent-plugin.d.ts +5 -2
  63. package/dist/cli/agent-plugin.js +17 -9
  64. package/dist/cli/code-agent-executor.js +25 -5
  65. package/dist/cli/code-agent-mcp-config.d.ts +21 -0
  66. package/dist/cli/code-agent-mcp-config.js +103 -0
  67. package/dist/client/NewWorkspaceAppFlow.js +4 -1
  68. package/dist/client/agent-chat/index.d.ts +4 -1
  69. package/dist/client/agent-chat/index.js +4 -1
  70. package/dist/client/agent-page/AgentTabsPage.d.ts +1 -0
  71. package/dist/client/agent-page/AgentTabsPage.js +4 -3
  72. package/dist/client/app-providers.d.ts +3 -1
  73. package/dist/client/app-providers.js +50 -5
  74. package/dist/client/chat/runtime.js +63 -0
  75. package/dist/client/chat-first/agents-pane.d.ts +2 -0
  76. package/dist/client/chat-first/agents-pane.js +6 -0
  77. package/dist/client/chat-first/app-pane.d.ts +2 -0
  78. package/dist/client/chat-first/app-pane.js +6 -0
  79. package/dist/client/chat-first/apps-rail.d.ts +2 -0
  80. package/dist/client/chat-first/apps-rail.js +103 -0
  81. package/dist/client/chat-first/browser-pane.d.ts +2 -0
  82. package/dist/client/chat-first/browser-pane.js +64 -0
  83. package/dist/client/chat-first/chat-history.d.ts +12 -0
  84. package/dist/client/chat-first/chat-history.js +10 -0
  85. package/dist/client/chat-first/copy.d.ts +2 -0
  86. package/dist/client/chat-first/copy.js +117 -0
  87. package/dist/client/chat-first/index.d.ts +13 -0
  88. package/dist/client/chat-first/index.js +12 -0
  89. package/dist/client/chat-first/primary-nav.d.ts +8 -0
  90. package/dist/client/chat-first/primary-nav.js +6 -0
  91. package/dist/client/chat-first/session-watch-pane.d.ts +2 -0
  92. package/dist/client/chat-first/session-watch-pane.js +9 -0
  93. package/dist/client/chat-first/surface-panel.d.ts +9 -0
  94. package/dist/client/chat-first/surface-panel.js +9 -0
  95. package/dist/client/chat-first/surface-tabs.d.ts +9 -0
  96. package/dist/client/chat-first/surface-tabs.js +74 -0
  97. package/dist/client/chat-first/types.d.ts +74 -0
  98. package/dist/client/chat-first/types.js +1 -0
  99. package/dist/client/chat-first-agent-activity.d.ts +11 -0
  100. package/dist/client/chat-first-agent-activity.js +51 -0
  101. package/dist/client/chat-first-surface-panel-toggle.d.ts +5 -0
  102. package/dist/client/chat-first-surface-panel-toggle.js +6 -0
  103. package/dist/client/chat-first.d.ts +208 -0
  104. package/dist/client/chat-first.js +816 -0
  105. package/dist/client/extensions/ExtensionViewer.js +8 -1
  106. package/dist/client/resources/McpIntegrationDialog.js +3 -2
  107. package/dist/client/resources/McpServerDetail.js +6 -11
  108. package/dist/client/resources/index.d.ts +1 -0
  109. package/dist/client/resources/index.js +1 -0
  110. package/dist/client/resources/use-mcp-servers.d.ts +20 -0
  111. package/dist/client/resources/use-mcp-servers.js +82 -46
  112. package/dist/client/settings/index.d.ts +1 -0
  113. package/dist/client/settings/index.js +1 -0
  114. package/dist/collab/awareness.d.ts +2 -2
  115. package/dist/collab/routes.d.ts +1 -1
  116. package/dist/collab/struct-routes.d.ts +1 -1
  117. package/dist/jobs/background-automation-runner.js +19 -2
  118. package/dist/jobs/scheduler-health.d.ts +20 -0
  119. package/dist/jobs/scheduler-health.js +86 -1
  120. package/dist/jobs/scheduler.js +38 -1
  121. package/dist/mcp-client/routes.d.ts +3 -0
  122. package/dist/mcp-client/routes.js +14 -0
  123. package/dist/notifications/routes.d.ts +1 -1
  124. package/dist/observability/routes.d.ts +5 -5
  125. package/dist/progress/routes.d.ts +1 -1
  126. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  127. package/dist/resources/handlers.d.ts +1 -1
  128. package/dist/secrets/routes.d.ts +3 -3
  129. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  130. package/dist/server/agents-bundle.d.ts +2 -0
  131. package/dist/server/agents-bundle.js +31 -2
  132. package/dist/server/credential-provider.d.ts +8 -2
  133. package/dist/server/credential-provider.js +13 -9
  134. package/dist/server/realtime-token.d.ts +1 -1
  135. package/dist/server/transcribe-voice.d.ts +1 -1
  136. package/dist/shared/chat-first-app-creation.d.ts +19 -0
  137. package/dist/shared/chat-first-app-creation.js +75 -0
  138. package/dist/shared/document-title.d.ts +7 -0
  139. package/dist/shared/document-title.js +29 -0
  140. package/dist/shared/index.d.ts +2 -0
  141. package/dist/shared/index.js +2 -0
  142. package/dist/styles/chat-history-list.css +16 -0
  143. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  144. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  145. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  146. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
  147. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
  148. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  149. package/dist/templates/default/react-router.config.ts +18 -0
  150. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
  151. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  152. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
  153. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
  154. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  155. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
  156. package/package.json +3 -2
  157. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
  158. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
  159. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  160. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
  161. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
  162. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  163. package/src/templates/default/react-router.config.ts +18 -0
  164. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
  165. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  166. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
  167. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
  168. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
  169. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
@@ -81,6 +81,13 @@ or other file-like payloads, design the upload path in the same change:
81
81
  provider upload first, then URL/id/blob handle in SQL. Do not add base64/binary
82
82
  columns or stuff files into `application_state`.
83
83
 
84
+ If the feature adds a server-side dependency carrying a native binary or heavy
85
+ runtime — headless browser, ffmpeg, image/video processing, ML runtime — read
86
+ the `performance` skill §9 before adding it. A deployed app has one `/*` page
87
+ function that every visitor's cache miss wakes, and it ships whatever the server
88
+ bundle depends on. Run that work from a background function or a job so the page
89
+ function never carries the weight.
90
+
84
91
  **If the action produces or lists a navigable resource**, add a `link` builder that returns `{ url: buildDeepLink({ app, view, params }), label }`. External coding agents and MCP hosts (Claude / ChatGPT / Claude Code / Cowork / Codex, over MCP/A2A) then surface an "Open in … →" deep link that drops the user back into the running UI focused on the record — for free. If a compatible MCP host should render an inline review/edit surface, also add `mcpApp` with `embedApp()` so the action embeds the real React app route instead of a one-off HTML UI. The `link` builder and `mcpApp` metadata must be pure and synchronous (no I/O). Any external-agent read/ingest action must be `http: { method: "GET" }` + `readOnly: true` + `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`. See the `external-agents` skill.
85
92
 
86
93
  ### 3. Skills / Instructions
@@ -195,4 +202,6 @@ TL;DR: declare the vocabulary once with `defineAppRoles({ appId, roles, defaultR
195
202
  - **create-skill** — How to create skills for new patterns (area 3 in detail)
196
203
  - **storing-data** — Where to store the feature's data
197
204
  - **real-time-sync** — How the UI stays in sync when the agent writes data
205
+ - **performance** — Query/load cost, and (§9) cold-start artifact size when a
206
+ feature adds a server-side dependency
198
207
  - **internationalization** — How to update localized UI copy and catalogs
@@ -3,8 +3,9 @@ name: turn-into-app
3
3
  description: >-
4
4
  Turn visible project context, a proven thread, skill, or workflow into a
5
5
  runnable Agent-Native app with simple buttons, visible agent steps, preview,
6
- and deployment handoff. Use when a user invokes `/turn-into-app` or asks to
7
- make a workflow into an app, including from Claude or ChatGPT on the web.
6
+ and deployment handoff. Use when a user invokes `/turn-into-app`,
7
+ `/make-into-app`, or asks to make a workflow into an app, including from
8
+ Claude or ChatGPT on the web.
8
9
  user-invocable: true
9
10
  scope: both
10
11
  metadata:
@@ -38,6 +39,15 @@ The build location depends on the host. Treat this as a hard routing rule:
38
39
  workspace, follow the local implementation steps below. The local agent may
39
40
  scaffold, edit, run, and verify the app there.
40
41
 
42
+ Once the source brief is sufficient to identify a repeatable workflow, the
43
+ handoff is non-interactive. Do not ask the user for visual, product, copy,
44
+ layout, template, integration, or implementation choices that can be resolved
45
+ from the source. Select the source's recommended option; otherwise choose the
46
+ most direct conventional default and record the assumption for later review.
47
+ Only stop for a genuine hard blocker such as missing authorization, a
48
+ destructive external action, an ambiguous target workspace, or no identifiable
49
+ workflow at all.
50
+
41
51
  ## Default behavior
42
52
 
43
53
  For a local code-agent runtime this is an end-to-end build skill, not a request
@@ -104,14 +114,16 @@ before creating the app. Read the host-provided context in this order:
104
114
  Record the brief with these headings before handoff: source and provenance,
105
115
  project goal, configuration and constraints, knowledge sources, repeatable
106
116
  workflow, inputs and outputs, judgment and review points, representative runs,
107
- integrations and permissions, and unknowns. This is the compact contract for
117
+ integrations and permissions, and unknowns and assumptions. This is the compact contract for
108
118
  the app. It keeps the new app useful without pretending that hidden Project
109
119
  history was imported. See [the fresh Project reference](references/fresh-project.md)
110
120
  for the host setup and brief template.
111
121
 
112
- If the visible Project context has no concrete repeatable job, ask for one
113
- focused clarification or a representative artifact. Do not fall back to a
114
- generic “what app do you want to make?” builder.
122
+ If the visible Project context has no concrete repeatable job and no primary
123
+ goal can be inferred, ask for one focused clarification or a representative
124
+ artifact. Otherwise use the project's primary goal and source conventions; do
125
+ not ask a questionnaire and do not fall back to a generic “what app do you
126
+ want to make?” builder.
115
127
 
116
128
  ## Source selection guard
117
129
 
@@ -36,7 +36,7 @@ Inputs and outputs:
36
36
  Judgment and review points:
37
37
  Representative runs: none, or 1-3 selected examples
38
38
  Integrations and permissions:
39
- Unknowns and follow-up:
39
+ Unknowns and assumptions:
40
40
  ```
41
41
 
42
42
  Keep source references bounded. Prefer a file name, URL, resource ID, or short
@@ -69,6 +69,13 @@ do not fall back to a local sandbox build. Claude Code and Codex Code are the
69
69
  local-agent exception and may follow the implementation steps in the main
70
70
  skill.
71
71
 
72
+ The brief is an implementation authorization, not a questionnaire. After a
73
+ repeatable workflow is identifiable, choose any source-recommended option
74
+ automatically. For unresolved non-blocking choices, use the most direct
75
+ conventional default, record it under assumptions, and continue. Ask only when
76
+ there is no identifiable workflow, no authorized target workspace, or a
77
+ destructive/credential boundary genuinely prevents the handoff.
78
+
72
79
  In a new Project chat, say:
73
80
 
74
81
  ```text
@@ -17,4 +17,14 @@ export declare const BUILDER_CAPABILITIES: EngineCapabilities;
17
17
  export declare const BUILDER_SUPPORTED_MODELS: readonly ["auto", string, "gpt-5-6-terra", "gpt-5-6-sol", "claude-haiku-4-5", "claude-sonnet-4-6" | "claude-sonnet-5", "claude-opus-4-8", "gemini-3-5-flash", "gemini-3-1-pro"];
18
18
  export declare const BUILDER_MODEL_UNAUTHORIZED_ERROR_CODE = "builder_model_unauthorized";
19
19
  export declare const BUILDER_DEFAULT_MODEL: string;
20
- export declare function createBuilderEngine(_config?: Record<string, unknown>): AgentEngine;
20
+ /**
21
+ * Credentials captured by a durable caller that cannot rely on ambient
22
+ * request context staying attached to a later run-manager callback.
23
+ */
24
+ export interface BuilderEngineCredentials {
25
+ privateKey: string | null;
26
+ publicKey: string | null;
27
+ userId?: string | null;
28
+ orgName?: string | null;
29
+ }
30
+ export declare function createBuilderEngine(config?: Record<string, unknown>): AgentEngine;
@@ -95,13 +95,17 @@ async function buildUpgradeUrl() {
95
95
  return url.toString();
96
96
  }
97
97
  class BuilderEngine {
98
+ configuredCredentials;
98
99
  name = "builder";
99
100
  label = "Builder.io Gateway";
100
101
  defaultModel = BUILDER_DEFAULT_MODEL;
101
102
  supportedModels = BUILDER_SUPPORTED_MODELS;
102
103
  capabilities = BUILDER_CAPABILITIES;
104
+ constructor(configuredCredentials) {
105
+ this.configuredCredentials = configuredCredentials;
106
+ }
103
107
  async *stream(opts) {
104
- const creds = await resolveBuilderCredentials();
108
+ const creds = this.configuredCredentials ?? (await resolveBuilderCredentials());
105
109
  const authHeader = creds.privateKey ? `Bearer ${creds.privateKey}` : null;
106
110
  const spaceId = creds.publicKey;
107
111
  const builderUserId = creds.userId;
@@ -255,7 +259,7 @@ class BuilderEngine {
255
259
  // self-healing path for workspace/env-managed credentials, which never
256
260
  // flow through writeBuilderCredentials.
257
261
  try {
258
- const creds = await resolveBuilderCredentials();
262
+ const creds = this.configuredCredentials ?? (await resolveBuilderCredentials());
259
263
  await clearBuilderCredentialAuthFailure({
260
264
  privateKey: creds.privateKey,
261
265
  publicKey: creds.publicKey,
@@ -765,8 +769,11 @@ function htmlToText(html) {
765
769
  .replace(/\n{3,}/g, "\n\n")
766
770
  .trim();
767
771
  }
768
- export function createBuilderEngine(_config = {}) {
769
- return new BuilderEngine();
772
+ export function createBuilderEngine(config = {}) {
773
+ const configuredCredentials = config.credentials && typeof config.credentials === "object"
774
+ ? config.credentials
775
+ : undefined;
776
+ return new BuilderEngine(configuredCredentials);
770
777
  }
771
778
  function resolveMaxBuilderGatewayTimeoutMs() {
772
779
  if (isInBackgroundFunctionRuntime()) {
@@ -73,6 +73,8 @@ export declare function createThread(ownerEmail: string, opts?: {
73
73
  title?: string;
74
74
  scope?: ChatThreadScope | null;
75
75
  source?: ChatThreadSource | null;
76
+ /** Explicit owner organization for durable/background callers. */
77
+ orgId?: string | null;
76
78
  }): Promise<ChatThread>;
77
79
  export declare function registerChatThreadsShareable(): void;
78
80
  export declare function ensureChatThreadTables(): Promise<void>;
@@ -367,7 +367,7 @@ export async function createThread(ownerEmail, opts) {
367
367
  const title = opts?.title ?? "";
368
368
  const scope = opts?.scope ?? null;
369
369
  const source = opts?.source ?? null;
370
- const orgId = getRequestOrgId() ?? null;
370
+ const orgId = opts?.orgId ?? getRequestOrgId() ?? null;
371
371
  await client.execute({
372
372
  sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, source_platform, source_app_id, source_url, org_id, visibility) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'private')`,
373
373
  args: [
@@ -2,8 +2,9 @@
2
2
  * Import and validate the portable Agent Plugin format.
3
3
  *
4
4
  * The importer intentionally handles only local plugin directories. Skills are
5
- * copied as inert files, and remote Streamable HTTP MCP entries are translated
6
- * to the Agent-Native `mcp.config.json` shape without copying package headers.
5
+ * copied as inert files into the workspace's agent-visible `.agents/skills`
6
+ * tree, and remote Streamable HTTP MCP entries are translated to the
7
+ * Agent-Native `mcp.config.json` shape without copying package headers.
7
8
  * A plugin package is not a credential store and this command never executes
8
9
  * a plugin's scripts or stdio servers.
9
10
  */
@@ -62,6 +63,8 @@ export interface LoadedAgentPlugin {
62
63
  }
63
64
  export interface AgentPluginImportOptions {
64
65
  targetDir?: string;
66
+ /** Optional skills root used by hosts whose agent cwd is a child workspace. */
67
+ skillsTargetDir?: string;
65
68
  force?: boolean;
66
69
  dryRun?: boolean;
67
70
  }
@@ -2,8 +2,9 @@
2
2
  * Import and validate the portable Agent Plugin format.
3
3
  *
4
4
  * The importer intentionally handles only local plugin directories. Skills are
5
- * copied as inert files, and remote Streamable HTTP MCP entries are translated
6
- * to the Agent-Native `mcp.config.json` shape without copying package headers.
5
+ * copied as inert files into the workspace's agent-visible `.agents/skills`
6
+ * tree, and remote Streamable HTTP MCP entries are translated to the
7
+ * Agent-Native `mcp.config.json` shape without copying package headers.
7
8
  * A plugin package is not a credential store and this command never executes
8
9
  * a plugin's scripts or stdio servers.
9
10
  */
@@ -42,7 +43,7 @@ Usage:
42
43
  Commands:
43
44
  import Import a standard Agent Plugin's Skills and remote MCP servers into an Agent-Native workspace.
44
45
 
45
- The importer copies Skills into a namespaced skills/<plugin>/<skill> path and
46
+ The importer copies Skills into a namespaced .agents/skills/<plugin>/<skill> path and
46
47
  maps Streamable HTTP MCP servers into mcp.config.json. Package headers are
47
48
  never imported as credentials; configure authentication through the app's
48
49
  normal Connections flow. Stdio and SSE servers are reported and skipped.`;
@@ -691,10 +692,10 @@ function planMcpServers(plugin, existing, force) {
691
692
  }
692
693
  return planned;
693
694
  }
694
- function planSkills(plugin, targetDir, targetPluginSlug, force) {
695
+ function planSkills(plugin, skillsRoot, force) {
695
696
  return plugin.skills.map((skill) => {
696
- const destination = path.join(targetDir, "skills", targetPluginSlug, skill.name);
697
- assertPathInside(targetDir, destination, "Imported skill destination");
697
+ const destination = path.join(skillsRoot, skill.name);
698
+ assertPathInside(skillsRoot, destination, "Imported skill destination");
698
699
  assertPathOutside(plugin.rootDir, destination, "Imported skill destination");
699
700
  assertNoSymlinkInExistingPath(path.dirname(destination), "Imported skill destination");
700
701
  if (!pathExists(destination)) {
@@ -878,12 +879,19 @@ export function importAgentPlugin(input, options = {}) {
878
879
  }
879
880
  assertNoSymlinkInExistingPath(targetDir, "Agent-Native import target");
880
881
  const slug = pluginSlug(plugin.manifest.name);
881
- const skillsRoot = path.join(targetDir, "skills", slug);
882
+ const skillsTargetDir = canonicalPathForComparison(options.skillsTargetDir ?? path.join(targetDir, ".agents", "skills"));
883
+ if (pathExists(skillsTargetDir) &&
884
+ !fs.statSync(skillsTargetDir).isDirectory()) {
885
+ throw new Error(`Agent-Native skills target must be a directory: ${skillsTargetDir}`);
886
+ }
887
+ assertPathOutside(plugin.rootDir, skillsTargetDir, "Imported skills target");
888
+ assertNoSymlinkInExistingPath(skillsTargetDir, "Imported skills target");
889
+ const skillsRoot = path.join(skillsTargetDir, slug);
882
890
  const metadataFile = path.join(targetDir, ".agent-native", "plugins", `${slug}.json`);
883
891
  const mcpFile = path.join(targetDir, "mcp.config.json");
884
- assertPathInside(targetDir, skillsRoot, "Imported skills root");
885
892
  assertPathInside(targetDir, metadataFile, "Imported metadata path");
886
893
  assertPathInside(targetDir, mcpFile, "Imported MCP config path");
894
+ assertPathInside(skillsTargetDir, skillsRoot, "Imported skills root");
887
895
  assertPathOutside(plugin.rootDir, skillsRoot, "Imported skills root");
888
896
  assertPathOutside(plugin.rootDir, metadataFile, "Imported metadata path");
889
897
  assertPathOutside(plugin.rootDir, mcpFile, "Imported MCP config path");
@@ -892,7 +900,7 @@ export function importAgentPlugin(input, options = {}) {
892
900
  const force = options.force ?? false;
893
901
  const dryRun = options.dryRun ?? false;
894
902
  const existingMcp = readExistingMcpConfig(mcpFile);
895
- const skillPlans = planSkills(plugin, targetDir, slug, force);
903
+ const skillPlans = planSkills(plugin, skillsRoot, force);
896
904
  const mcpPlans = planMcpServers(plugin, existingMcp, force);
897
905
  const existingMetadata = readExistingMetadata(metadataFile);
898
906
  const metadataIsSame = metadataMatches(existingMetadata, plugin);
@@ -14,6 +14,7 @@ import { readAgentsBundleFromFs, generateDevelopmentSkillsPromptBlock, } from ".
14
14
  import { getAmbientOrgId, getAmbientUserEmail, runWithRequestContext, } from "../server/request-context.js";
15
15
  import { isReasoningEffort, } from "../shared/reasoning-effort.js";
16
16
  import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
17
+ import { codexMcpConfigArgs, mergeCodeAgentMcpConfig, restrictCodeAgentMcpConfig, } from "./code-agent-mcp-config.js";
17
18
  import { createCodeAgentOutputSmoother } from "./code-agent-output-smoother.js";
18
19
  import { addCodeAgentCommandToAllowlist, appendCodeAgentTranscriptEvent, dequeueCodeAgentFollowUp, getCodeAgentRunRecord, isCodeAgentCommandAllowed, listCodeAgentTranscriptEvents, updateCodeAgentRunRecord, } from "./code-agent-runs.js";
19
20
  const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
@@ -443,20 +444,26 @@ async function executeCodexCliRun(options) {
443
444
  const model = normalizeCodexCliModel(options.model);
444
445
  const streamToolOutputToStdout = options.streamToolOutputToStdout ??
445
446
  process.env.AGENT_NATIVE_CODE_AGENT_STRUCTURED_STDOUT !== "1";
447
+ const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
446
448
  const args = [
449
+ ...codexMcpConfigArgs(),
447
450
  "--ask-for-approval",
448
451
  "never",
449
- "exec",
452
+ "--sandbox",
453
+ codexSandboxForPermissionMode(options.permissionMode),
450
454
  "--cd",
451
455
  cwd,
456
+ "exec",
452
457
  "--color",
453
458
  "never",
454
459
  "--skip-git-repo-check",
455
- "--sandbox",
456
- codexSandboxForPermissionMode(options.permissionMode),
460
+ "--ignore-user-config",
457
461
  "--output-last-message",
458
462
  outputPath,
459
463
  ];
464
+ if (additionalSkillsRoot && fs.existsSync(additionalSkillsRoot)) {
465
+ args.splice(args.indexOf("--output-last-message"), 0, "--add-dir", additionalSkillsRoot);
466
+ }
460
467
  if (model)
461
468
  args.push("--model", model);
462
469
  args.push("-");
@@ -663,9 +670,15 @@ function buildCodexCliPrompt(run, prompt) {
663
670
  const modeInstruction = mode === "Plan"
664
671
  ? "Inspect and explain only. Do not edit files or run mutating commands."
665
672
  : "Edit and verify as needed. Do not create, switch, reset, rebase, or stash git branches.";
673
+ const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
666
674
  return [
667
675
  `You are running from Agent-Native Code in ${run.cwd || process.cwd()}.`,
668
676
  "Follow the repository AGENTS.md and any relevant skill instructions.",
677
+ ...(additionalSkillsRoot
678
+ ? [
679
+ `Additional installed Agent Plugin skills are available under ${additionalSkillsRoot}. Read any relevant SKILL.md files before acting.`,
680
+ ]
681
+ : []),
669
682
  `Run mode: ${mode} (${permissionMode}). ${modeInstruction}`,
670
683
  "",
671
684
  "# User request",
@@ -933,9 +946,12 @@ async function startCodeAgentMcpManager(runId) {
933
946
  });
934
947
  return null;
935
948
  });
936
- if (!config || Object.keys(config.servers ?? {}).length === 0)
949
+ const effectiveConfig = restrictCodeAgentMcpConfig(mergeCodeAgentMcpConfig(config));
950
+ if (!effectiveConfig ||
951
+ Object.keys(effectiveConfig.servers ?? {}).length === 0) {
937
952
  return null;
938
- const manager = new McpClientManager(config);
953
+ }
954
+ const manager = new McpClientManager(effectiveConfig);
939
955
  await manager.start().catch((err) => {
940
956
  const message = err instanceof Error ? err.message : String(err);
941
957
  appendCodeAgentTranscriptEvent({
@@ -1299,6 +1315,10 @@ export async function buildCodeAgentSystemPrompt(cwd, permissionMode) {
1299
1315
  const appConfig = await loadResolvedAgentNativeConfig(cwd, createAgentNativeConfigContext("serve", "development"));
1300
1316
  const bundle = readAgentsBundleFromFs(cwd, null, {
1301
1317
  instructions: appConfig.instructions,
1318
+ additionalSkillDirs: (process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT ?? "")
1319
+ .split(path.delimiter)
1320
+ .map((value) => value.trim())
1321
+ .filter(Boolean),
1302
1322
  });
1303
1323
  // If the bundle has no AGENTS.md, try CLAUDE.md as a fallback — many repos
1304
1324
  // use that name for agent instructions (e.g. Claude Code projects).
@@ -0,0 +1,21 @@
1
+ import type { McpConfig } from "../mcp-client/config.js";
2
+ /**
3
+ * Keep a desktop coding run on the app registry's MCP servers. The normal
4
+ * server-side manager merges persisted MCP settings for web requests, but a
5
+ * local shell must not inherit arbitrary user MCP servers when the desktop
6
+ * host has supplied an explicit workspace allowlist.
7
+ */
8
+ export declare function restrictCodeAgentMcpConfig(config: McpConfig | null, environment?: NodeJS.ProcessEnv): McpConfig | null;
9
+ /**
10
+ * Add host-provided app/plugin servers to the normal merged config. The
11
+ * filesystem and environment layers have different precedence rules, so the
12
+ * child process combines them explicitly instead of silently dropping one.
13
+ */
14
+ export declare function mergeCodeAgentMcpConfig(config: McpConfig | null, environment?: NodeJS.ProcessEnv): McpConfig | null;
15
+ /**
16
+ * Convert the host-scoped HTTP MCP config into Codex CLI's `-c` overrides.
17
+ * The caller adds `--ignore-user-config` in the `exec` segment so a local
18
+ * coding session receives the workspace apps, not the user's global MCP
19
+ * catalog.
20
+ */
21
+ export declare function codexMcpConfigArgs(environment?: NodeJS.ProcessEnv): string[];
@@ -0,0 +1,103 @@
1
+ const DESKTOP_MCP_ALLOWLIST_ENV = "AGENT_NATIVE_CODE_AGENT_MCP_SERVER_ALLOWLIST";
2
+ function parseServerAllowlist(raw) {
3
+ if (raw === undefined)
4
+ return null;
5
+ return new Set(raw
6
+ .split(",")
7
+ .map((value) => value.trim())
8
+ .filter(Boolean));
9
+ }
10
+ /**
11
+ * Keep a desktop coding run on the app registry's MCP servers. The normal
12
+ * server-side manager merges persisted MCP settings for web requests, but a
13
+ * local shell must not inherit arbitrary user MCP servers when the desktop
14
+ * host has supplied an explicit workspace allowlist.
15
+ */
16
+ export function restrictCodeAgentMcpConfig(config, environment = process.env) {
17
+ const allowlist = parseServerAllowlist(environment[DESKTOP_MCP_ALLOWLIST_ENV]);
18
+ if (!allowlist || !config)
19
+ return config;
20
+ const servers = Object.fromEntries(Object.entries(config.servers).filter(([id]) => allowlist.has(id)));
21
+ return { ...config, servers };
22
+ }
23
+ function codexConfigKey(serverId) {
24
+ const normalized = serverId.replace(/[^A-Za-z0-9_-]/g, "_");
25
+ return normalized || "workspace";
26
+ }
27
+ function tomlString(value) {
28
+ return JSON.stringify(value);
29
+ }
30
+ function tomlInlineTable(headers) {
31
+ return `{${Object.entries(headers)
32
+ .map(([key, value]) => `${tomlString(key)}=${tomlString(value)}`)
33
+ .join(",")}}`;
34
+ }
35
+ function parseMcpServers(environment) {
36
+ const raw = environment.MCP_SERVERS?.trim();
37
+ if (!raw)
38
+ return {};
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ throw new Error("MCP_SERVERS is not valid JSON.");
45
+ }
46
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
47
+ throw new Error("MCP_SERVERS must contain a servers object.");
48
+ }
49
+ const serverMap = "servers" in parsed &&
50
+ parsed.servers &&
51
+ typeof parsed.servers === "object" &&
52
+ !Array.isArray(parsed.servers)
53
+ ? parsed.servers
54
+ : parsed;
55
+ return Object.fromEntries(Object.entries(serverMap).map(([id, value]) => {
56
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
57
+ throw new Error(`MCP_SERVERS entry ${id} is invalid.`);
58
+ }
59
+ return [id, value];
60
+ }));
61
+ }
62
+ /**
63
+ * Add host-provided app/plugin servers to the normal merged config. The
64
+ * filesystem and environment layers have different precedence rules, so the
65
+ * child process combines them explicitly instead of silently dropping one.
66
+ */
67
+ export function mergeCodeAgentMcpConfig(config, environment = process.env) {
68
+ const environmentServers = parseMcpServers(environment);
69
+ if (Object.keys(environmentServers).length === 0)
70
+ return config;
71
+ return {
72
+ ...(config ?? { servers: {} }),
73
+ servers: {
74
+ ...(config?.servers ?? {}),
75
+ ...environmentServers,
76
+ },
77
+ source: config?.source
78
+ ? `${config.source}+desktop-environment`
79
+ : "env:MCP_SERVERS",
80
+ };
81
+ }
82
+ /**
83
+ * Convert the host-scoped HTTP MCP config into Codex CLI's `-c` overrides.
84
+ * The caller adds `--ignore-user-config` in the `exec` segment so a local
85
+ * coding session receives the workspace apps, not the user's global MCP
86
+ * catalog.
87
+ */
88
+ export function codexMcpConfigArgs(environment = process.env) {
89
+ const servers = parseMcpServers(environment);
90
+ if (Object.keys(servers).length === 0)
91
+ return [];
92
+ const args = [];
93
+ for (const [serverId, server] of Object.entries(servers)) {
94
+ if (server.type !== "http" || !server.url)
95
+ continue;
96
+ const key = codexConfigKey(serverId);
97
+ args.push("-c", `mcp_servers.${tomlString(key)}.url=${tomlString(server.url)}`);
98
+ if (server.headers && Object.keys(server.headers).length > 0) {
99
+ args.push("-c", `mcp_servers.${tomlString(key)}.http_headers=${tomlInlineTable(server.headers)}`);
100
+ }
101
+ }
102
+ return args;
103
+ }
@@ -43,6 +43,7 @@ const ERROR_FAILURE_REASONS = new Set([
43
43
  "builder-not-connected",
44
44
  "credential-store-unavailable",
45
45
  ]);
46
+ const LOCAL_APP_DOCS_URL = "https://agent-native.com/docs/multi-app-workspace#adding-a-new-app";
46
47
  function isErrorFailureReason(reason) {
47
48
  return !!reason && ERROR_FAILURE_REASONS.has(reason);
48
49
  }
@@ -267,7 +268,9 @@ export function NewWorkspaceAppFlow({ sourceApp = "chat", className = "", dispat
267
268
  }
268
269
  return (_jsx("section", { className: `mx-auto flex w-full max-w-5xl flex-col gap-5 px-4 py-6 ${className}`, children: _jsxs("div", { className: "grid gap-5 lg:grid-cols-[minmax(0,1fr)_320px]", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(PromptComposer, { autoFocus: true, disabled: isSubmitting, placeholder: "Describe the app your teammate should be able to use...", draftScope: "dispatch:new-app", preserveDraftOnSubmit: true, onSubmit: (text) => submit(text) }), statusMessage ? (_jsxs("div", { className: `flex flex-col gap-2 rounded-md border px-3 py-2 text-sm ${isErrorFailureReason(failureReason)
269
270
  ? "border-destructive/40 bg-destructive/10 text-destructive"
270
- : "border-border bg-muted/40 text-muted-foreground"}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [isErrorFailureReason(failureReason) ? (_jsx(IconAlertTriangle, { className: "h-4 w-4 shrink-0" })) : null, _jsx("span", { children: statusMessage }), branchUrl ? (_jsxs("a", { href: branchUrl, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1 font-medium text-foreground underline", children: ["Open branch ", _jsx(IconArrowUpRight, { className: "h-3 w-3" })] })) : null] }), failureReason === "builder-not-connected" ? (_jsx("button", { type: "button", onClick: () => connectFlow.start(), disabled: connectFlow.connecting, className: "inline-flex w-fit cursor-pointer items-center gap-1 rounded-md border border-border bg-background px-2.5 py-1 text-xs font-medium text-foreground hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-60", children: connectFlow.connecting ? "Connecting..." : "Connect Builder" })) : null, failureReason === "credential-store-unavailable" ||
271
+ : "border-border bg-muted/40 text-muted-foreground"}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [isErrorFailureReason(failureReason) ? (_jsx(IconAlertTriangle, { className: "h-4 w-4 shrink-0" })) : null, _jsx("span", { children: statusMessage }), branchUrl ? (_jsxs("a", { href: branchUrl, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1 font-medium text-foreground underline", children: ["Open branch ", _jsx(IconArrowUpRight, { className: "h-3 w-3" })] })) : null] }), failureReason === "builder-not-connected" ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => connectFlow.start(), disabled: connectFlow.connecting, className: "inline-flex w-fit cursor-pointer items-center gap-1 rounded-md border border-border bg-background px-2.5 py-1 text-xs font-medium text-foreground hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-60", children: connectFlow.connecting
272
+ ? "Connecting..."
273
+ : "Connect Builder" }), _jsxs("a", { href: LOCAL_APP_DOCS_URL, target: "_blank", rel: "noreferrer", "data-create-app-local-link": true, className: "inline-flex items-center gap-1 text-xs font-medium text-foreground underline underline-offset-2", children: ["Create locally ", _jsx(IconArrowUpRight, { className: "h-3 w-3" })] })] })) : null, failureReason === "credential-store-unavailable" ||
271
274
  failureReason === "builder-error" ? (_jsx("button", { type: "button", onClick: () => submit(lastSubmittedPrompt), disabled: isSubmitting, className: "inline-flex w-fit cursor-pointer items-center gap-1 rounded-md border border-border bg-background px-2.5 py-1 text-xs font-medium text-foreground hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-60", children: "Try again" })) : null] })) : null] }), _jsxs("aside", { className: "overflow-hidden rounded-lg border border-border bg-card", children: [_jsx("div", { className: "border-b border-border px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(IconKey, { className: "h-4 w-4" }), "Dispatch keys"] }), _jsx("span", { className: "shrink-0 rounded border border-border bg-background/40 px-2 py-0.5 text-[11px] text-muted-foreground", children: selectedSecretLabel })] }) }), _jsx("div", { className: "max-h-[220px] space-y-2 overflow-y-auto p-3", children: vaultAccessMode === "all-apps" ? (_jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground", children: "Every saved Dispatch vault key is available to new apps." })) : secretsError ? (_jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground", children: secretsError })) : secrets.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground", children: "No Dispatch vault keys found yet." })) : (secrets.map((secret) => {
272
275
  const selected = selectedSecretIds.includes(secret.id);
273
276
  return (_jsxs("div", { className: `group rounded-md border text-sm transition ${selected
@@ -11,6 +11,8 @@ export { CREDENTIAL_GAP_SIGNAL, isCredentialGapCodeAgentEvent, } from "../../cod
11
11
  export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
12
12
  export { useChatModels, type UseChatModelsResult, type EngineModelGroup, } from "../use-chat-models.js";
13
13
  export { CodeRequiredDialog, type CodeRequiredDialogProps, } from "../components/CodeRequiredDialog.js";
14
+ export { ChatFirstAgentActivityPanel, type ChatFirstAgentActivityPanelProps, } from "../chat-first-agent-activity.js";
15
+ export { ChatFirstSurfacePanelToggle } from "../chat-first-surface-panel-toggle.js";
14
16
  export { useAgentEngineConfigured, type AgentEngineConfiguredState, type UseAgentEngineConfiguredResult, } from "../use-agent-engine-configured.js";
15
17
  export { BuilderSetupCard } from "../chat/run-recovery.js";
16
18
  export { AgentConversation, AgentConversationMessageView, normalizeCodeAgentTranscriptForConversation, useNearBottomAutoscroll, type CodeAgentConversationTranscriptEvent, type CodeAgentConversationTranscriptEventType, type NormalizeCodeAgentTranscriptOptions, type AgentConversationArtifact, type AgentConversationAttachment, type AgentConversationMessage, type AgentConversationMessagePart, type AgentConversationMessageRole, type AgentConversationNotice, type AgentConversationNoticeTone, type AgentConversationToolCall, type AgentConversationToolState, } from "../conversation/index.js";
@@ -29,7 +31,7 @@ export { useChatThreads, type ChatThreadScope, type ChatThreadSnapshot, type Cha
29
31
  export { ChatHistoryList, type ChatHistoryItem, type ChatHistorySection, type ChatHistoryListProps, } from "../chat/ChatHistoryList.js";
30
32
  export { AgentChatHome, type AgentChatHomeProps } from "../AgentChatHome.js";
31
33
  export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, type AgentChatSurfaceMode, type AgentChatSurfaceProps, type AgentPanelProps, type AgentSidebarProps, } from "../AgentPanel.js";
32
- export { AgentTabsPage, type AgentPageExtraTabContext, type AgentPageExtraTabFactory, type AgentTabsPageProps, } from "../agent-page/AgentTabsPage.js";
34
+ export { AgentTabsPage, ConnectionsTab, type AgentPageExtraTabContext, type AgentPageExtraTabFactory, type AgentTabsPageProps, } from "../agent-page/AgentTabsPage.js";
33
35
  export type { AgentPageScope, AgentPageTabProps } from "../agent-page/types.js";
34
36
  export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, type AgentChatHomeHandoffOptions, type AgentChatViewTransition, type AgentChatViewTransitionOptions, } from "../chat-view-transition.js";
35
37
  export { useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, type UseAgentChatHomeHandoffLinksOptions, type UseAgentChatHomeHandoffOptions, } from "../use-agent-chat-home-handoff.js";
@@ -37,3 +39,4 @@ export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR
37
39
  export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registerActionChatRenderer, registerFallbackToolRenderer, registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, registerReservedToolRenderer, registerToolRenderer, resolveToolRenderer, type ActionChatRendererRegistration, type ToolRendererComponent, type ToolRendererContext, type ToolRendererMatch, type ToolRendererProps, type ToolRendererRegistration, } from "../chat/tool-render-registry.js";
38
40
  export * from "../chat/connectors.js";
39
41
  export * from "../chat/runtime.js";
42
+ export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, type ChatFirstAppLayoutPreference, type ChatFirstModeReadResult, type ChatFirstAppRegistration, type ChatFirstAppResolution, type ChatFirstAppTarget, type ChatFirstAgentActivity, type ChatFirstAgentActivityStatus, type ChatFirstOpenAppDelivery, type ChatFirstOpenAppDetail, type ChatFirstOpenBrowserDetail, type ChatFirstBrowserResolution, type ChatFirstSessionKind, type ChatFirstSessionReference, type ChatFirstSessionWatchDelivery, type ChatFirstSessionWatchState, type ChatFirstSessionWatchStore, type ChatFirstSurfaceKind, type ChatFirstSurfaceTab, type ChatFirstSurfacePanelState, type ChatFirstSurfacePanelStore, type ChatFirstSurfaceTabsState, type ChatFirstSurfaceTabsStore, } from "../chat-first.js";
@@ -11,6 +11,8 @@ export { CREDENTIAL_GAP_SIGNAL, isCredentialGapCodeAgentEvent, } from "../../cod
11
11
  export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
12
12
  export { useChatModels, } from "../use-chat-models.js";
13
13
  export { CodeRequiredDialog, } from "../components/CodeRequiredDialog.js";
14
+ export { ChatFirstAgentActivityPanel, } from "../chat-first-agent-activity.js";
15
+ export { ChatFirstSurfacePanelToggle } from "../chat-first-surface-panel-toggle.js";
14
16
  export { useAgentEngineConfigured, } from "../use-agent-engine-configured.js";
15
17
  export { BuilderSetupCard } from "../chat/run-recovery.js";
16
18
  export { AgentConversation, AgentConversationMessageView, normalizeCodeAgentTranscriptForConversation, useNearBottomAutoscroll, } from "../conversation/index.js";
@@ -29,10 +31,11 @@ export { useChatThreads, } from "../use-chat-threads.js";
29
31
  export { ChatHistoryList, } from "../chat/ChatHistoryList.js";
30
32
  export { AgentChatHome } from "../AgentChatHome.js";
31
33
  export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, } from "../AgentPanel.js";
32
- export { AgentTabsPage, } from "../agent-page/AgentTabsPage.js";
34
+ export { AgentTabsPage, ConnectionsTab, } from "../agent-page/AgentTabsPage.js";
33
35
  export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, } from "../chat-view-transition.js";
34
36
  export { useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, } from "../use-agent-chat-home-handoff.js";
35
37
  export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR_WIDE_WIDTH_RATIO, clampAgentSidebarWidth, getAgentSidebarMaxWidth, getAgentSidebarWideWidth, requestAgentSidebarOpen, SIDEBAR_STATE_CHANGE_EVENT, setAgentSidebarOpenPreference, } from "../agent-sidebar-state.js";
36
38
  export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registerActionChatRenderer, registerFallbackToolRenderer, registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, registerReservedToolRenderer, registerToolRenderer, resolveToolRenderer, } from "../chat/tool-render-registry.js";
37
39
  export * from "../chat/connectors.js";
38
40
  export * from "../chat/runtime.js";
41
+ export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, } from "../chat-first.js";
@@ -3,6 +3,7 @@ import type { ResourceView } from "../resources/ResourcesPanel.js";
3
3
  import type { SettingsTabItem } from "../settings/SettingsTabsPage.js";
4
4
  import type { AgentPageTabProps } from "./types.js";
5
5
  export declare const AGENT_RESOURCE_DOCS_HREF: Record<ResourceView, string>;
6
+ export declare function ConnectionsTab({ canManageOrg, }?: Partial<AgentPageTabProps>): import("react").JSX.Element;
6
7
  export declare const AGENT_ACCESS_DOCS_HREF: {
7
8
  readonly mcp: "https://agent-native.com/docs/mcp-protocol";
8
9
  readonly a2a: "https://agent-native.com/docs/a2a-protocol";
@@ -113,7 +113,7 @@ function ServerStatus({ server }) {
113
113
  }
114
114
  return (_jsx("span", { className: "text-[11px] text-muted-foreground", children: "Status unknown" }));
115
115
  }
116
- function ConnectionsTab({ canManageOrg = false }) {
116
+ export function ConnectionsTab({ canManageOrg, } = {}) {
117
117
  const t = useT();
118
118
  const serversQuery = useMcpServers();
119
119
  const createServer = useCreateMcpServer();
@@ -123,8 +123,9 @@ function ConnectionsTab({ canManageOrg = false }) {
123
123
  const [deleteTarget, setDeleteTarget] = useState(null);
124
124
  const [error, setError] = useState(null);
125
125
  const data = serversQuery.data;
126
+ const resolvedCanManageOrg = canManageOrg ?? (data?.role === "owner" || data?.role === "admin");
126
127
  const hasOrg = Boolean(data?.orgId);
127
- const canCreateOrgMcp = hasOrg && canManageOrg;
128
+ const canCreateOrgMcp = hasOrg && resolvedCanManageOrg;
128
129
  const onCreateMcpServer = useCallback(async (args) => {
129
130
  if (args.scope === "org" && !canCreateOrgMcp) {
130
131
  throw new Error("Only organization admins can add organization agent integrations.");
@@ -152,7 +153,7 @@ function ConnectionsTab({ canManageOrg = false }) {
152
153
  };
153
154
  const renderServer = (server) => {
154
155
  const key = `${server.scope}:${server.id}`;
155
- const canDelete = server.scope === "user" || canManageOrg;
156
+ const canDelete = server.scope === "user" || resolvedCanManageOrg;
156
157
  const selected = selectedServer?.id === server.id && selectedServer.scope === server.scope;
157
158
  return (_jsxs("div", { className: cn("group/connection-row py-4 transition-colors first:pt-5 last:pb-5", selected && "bg-accent/20"), children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsxs("button", { type: "button", onClick: () => setSelectedServer(selected ? null : server), className: "min-w-0 flex-1 cursor-pointer text-start", children: [_jsx("div", { className: "flex flex-wrap items-center gap-2", children: _jsx("span", { className: "truncate text-sm font-medium text-foreground", children: server.name }) }), _jsx("code", { className: "mt-1 block truncate text-[11px] text-muted-foreground", children: server.url }), _jsxs("div", { className: "mt-2 flex items-center gap-2", children: [_jsx(ServerStatus, { server: server }), server.description && (_jsx("span", { className: "truncate text-[11px] text-muted-foreground/70", children: server.description }))] })] }), canDelete && (_jsx("button", { type: "button", onClick: () => void removeServer(server), disabled: deleteServer.isPending, className: cn("cursor-pointer rounded-md px-2 py-1 text-[11px] font-medium text-muted-foreground hover:bg-destructive/10 hover:text-destructive", deleteTarget === key && "bg-destructive/10 text-destructive"), children: deleteTarget === key ? "Confirm" : "Delete" }))] }), selected && (_jsx("div", { className: "mt-3 border-t border-border/70 pt-3", children: _jsx(McpServerDetail, { server: server }) }))] }, key));
158
159
  };
@@ -108,6 +108,8 @@ export interface AppProvidersProps {
108
108
  * its own scoped token. Public/SEO routes should use `isPublicPath` instead.
109
109
  */
110
110
  sessionBypass?: boolean;
111
+ /** Fallback used if route metadata leaves the browser title empty or structured. */
112
+ documentTitleFallback?: string;
111
113
  children: React.ReactNode;
112
114
  }
113
- export declare function AppProviders({ queryClient, isPublicPath, clientOnlyFallback, sessionBypass, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, children, }: AppProvidersProps): React.JSX.Element;
115
+ export declare function AppProviders({ queryClient, isPublicPath, clientOnlyFallback, sessionBypass, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, documentTitleFallback, children, }: AppProvidersProps): React.JSX.Element;