@agent-native/core 0.98.5 → 0.98.7

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 (135) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/external-agents.mdx +66 -7
  4. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +40 -7
  5. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +41 -7
  6. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +41 -7
  7. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +41 -7
  8. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +40 -7
  9. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +40 -7
  10. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +40 -7
  11. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +41 -7
  12. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +39 -7
  13. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +39 -7
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/a2a/handlers.ts +101 -26
  16. package/corpus/core/src/a2a/task-store.ts +128 -3
  17. package/corpus/core/src/client/session-replay.ts +67 -5
  18. package/corpus/core/src/integrations/adapters/slack.ts +207 -17
  19. package/corpus/core/src/integrations/identity-links-store.ts +210 -0
  20. package/corpus/core/src/integrations/identity.ts +196 -0
  21. package/corpus/core/src/integrations/index.ts +6 -0
  22. package/corpus/core/src/integrations/plugin.ts +130 -1
  23. package/corpus/core/src/integrations/types.ts +37 -0
  24. package/corpus/core/src/integrations/webhook-handler.ts +1 -0
  25. package/corpus/core/src/mcp/build-server.ts +127 -22
  26. package/corpus/core/src/mcp/builtin-tools.ts +5 -2
  27. package/corpus/core/src/mcp/external-agent-policy.ts +18 -0
  28. package/corpus/core/src/mcp/index.ts +1 -0
  29. package/corpus/core/src/server/agent-chat/plugin-options.ts +12 -1
  30. package/corpus/core/src/server/agent-chat/script-entries.ts +20 -3
  31. package/corpus/core/src/server/agent-chat-plugin.ts +3 -0
  32. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  33. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +4 -1
  34. package/corpus/templates/analytics/AGENTS.md +20 -5
  35. package/corpus/templates/analytics/actions/create-session-replay-agent-link.ts +0 -1
  36. package/corpus/templates/analytics/actions/get-session-replay-events.ts +0 -1
  37. package/corpus/templates/analytics/actions/get-session-replay-timeline.ts +40 -0
  38. package/corpus/templates/analytics/actions/list-error-issues.ts +12 -0
  39. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -1
  40. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +48 -17
  41. package/corpus/templates/analytics/changelog/2026-07-12-analytics-uses-the-full-in-app-agent-for-multi-step-incident.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-07-12-connected-external-agents-can-now-look-up-sessions-error-iss.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-07-12-session-identities-stay-visible-when-demo-mode-is-off.md +6 -0
  44. package/corpus/templates/analytics/server/handlers/session-replay.ts +15 -9
  45. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +2 -2
  46. package/corpus/templates/analytics/server/lib/error-capture.ts +47 -5
  47. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +72 -22
  48. package/corpus/templates/analytics/server/lib/session-replay.ts +41 -11
  49. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -0
  50. package/corpus/templates/analytics/server/plugins/db.ts +15 -0
  51. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +8 -5
  52. package/corpus/templates/analytics/server/routes/api/session-replay/agent-events.json.get.ts +8 -3
  53. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +12 -2
  54. package/corpus/templates/forms/app/global.css +54 -8
  55. package/corpus/templates/forms/app/pages/AskPage.tsx +35 -13
  56. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -17
  57. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +8 -5
  58. package/corpus/templates/forms/changelog/2026-07-12-ask-forms-suggestions-now-sit-beneath-the-composer-for-a-tig.md +6 -0
  59. package/corpus/templates/forms/changelog/2026-07-12-builder-response-tables-now-fill-the-view-without-a-redundan.md +6 -0
  60. package/corpus/templates/forms/changelog/2026-07-12-response-tables-now-fill-the-available-pane-with-a-flexible-.md +6 -0
  61. package/corpus/templates/mail/AGENTS.md +3 -1
  62. package/corpus/templates/mail/server/lib/mail-integrations.ts +3 -0
  63. package/dist/a2a/handlers.d.ts.map +1 -1
  64. package/dist/a2a/handlers.js +89 -20
  65. package/dist/a2a/handlers.js.map +1 -1
  66. package/dist/a2a/task-store.d.ts +32 -1
  67. package/dist/a2a/task-store.d.ts.map +1 -1
  68. package/dist/a2a/task-store.js +111 -4
  69. package/dist/a2a/task-store.js.map +1 -1
  70. package/dist/client/session-replay.d.ts.map +1 -1
  71. package/dist/client/session-replay.js +53 -8
  72. package/dist/client/session-replay.js.map +1 -1
  73. package/dist/collab/routes.d.ts +1 -1
  74. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  75. package/dist/integrations/adapters/slack.js +176 -17
  76. package/dist/integrations/adapters/slack.js.map +1 -1
  77. package/dist/integrations/identity-links-store.d.ts +23 -0
  78. package/dist/integrations/identity-links-store.d.ts.map +1 -0
  79. package/dist/integrations/identity-links-store.js +158 -0
  80. package/dist/integrations/identity-links-store.js.map +1 -0
  81. package/dist/integrations/identity.d.ts +28 -0
  82. package/dist/integrations/identity.d.ts.map +1 -0
  83. package/dist/integrations/identity.js +142 -0
  84. package/dist/integrations/identity.js.map +1 -0
  85. package/dist/integrations/index.d.ts +2 -0
  86. package/dist/integrations/index.d.ts.map +1 -1
  87. package/dist/integrations/index.js +2 -0
  88. package/dist/integrations/index.js.map +1 -1
  89. package/dist/integrations/plugin.d.ts.map +1 -1
  90. package/dist/integrations/plugin.js +93 -1
  91. package/dist/integrations/plugin.js.map +1 -1
  92. package/dist/integrations/types.d.ts +31 -0
  93. package/dist/integrations/types.d.ts.map +1 -1
  94. package/dist/integrations/types.js.map +1 -1
  95. package/dist/integrations/webhook-handler.js +1 -0
  96. package/dist/integrations/webhook-handler.js.map +1 -1
  97. package/dist/mcp/build-server.d.ts +9 -2
  98. package/dist/mcp/build-server.d.ts.map +1 -1
  99. package/dist/mcp/build-server.js +97 -20
  100. package/dist/mcp/build-server.js.map +1 -1
  101. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  102. package/dist/mcp/builtin-tools.js +5 -2
  103. package/dist/mcp/builtin-tools.js.map +1 -1
  104. package/dist/mcp/external-agent-policy.d.ts +19 -0
  105. package/dist/mcp/external-agent-policy.d.ts.map +1 -0
  106. package/dist/mcp/external-agent-policy.js +2 -0
  107. package/dist/mcp/external-agent-policy.js.map +1 -0
  108. package/dist/mcp/index.d.ts +1 -0
  109. package/dist/mcp/index.d.ts.map +1 -1
  110. package/dist/mcp/index.js.map +1 -1
  111. package/dist/notifications/routes.d.ts +1 -1
  112. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  113. package/dist/server/agent-chat/plugin-options.d.ts +11 -1
  114. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  115. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  116. package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
  117. package/dist/server/agent-chat/script-entries.js +16 -2
  118. package/dist/server/agent-chat/script-entries.js.map +1 -1
  119. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  120. package/dist/server/agent-chat-plugin.js +3 -0
  121. package/dist/server/agent-chat-plugin.js.map +1 -1
  122. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  123. package/docs/content/external-agents.mdx +66 -7
  124. package/docs/content/locales/ar-SA/external-agents.mdx +40 -7
  125. package/docs/content/locales/de-DE/external-agents.mdx +41 -7
  126. package/docs/content/locales/es-ES/external-agents.mdx +41 -7
  127. package/docs/content/locales/fr-FR/external-agents.mdx +41 -7
  128. package/docs/content/locales/hi-IN/external-agents.mdx +40 -7
  129. package/docs/content/locales/ja-JP/external-agents.mdx +40 -7
  130. package/docs/content/locales/ko-KR/external-agents.mdx +40 -7
  131. package/docs/content/locales/pt-BR/external-agents.mdx +41 -7
  132. package/docs/content/locales/zh-CN/external-agents.mdx +39 -7
  133. package/docs/content/locales/zh-TW/external-agents.mdx +39 -7
  134. package/package.json +1 -1
  135. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
@@ -324,8 +324,9 @@ MCP サーバーは、ホスト型コネクタ (ChatGPT、Claude)、コード
324
324
  </div>
325
325
  </div>
326
326
  <p class="diagram-muted note">
327
- <code>tool-search</code> reaches any full-tier tool on demand &mdash; so the
328
- compact default keeps context small without hiding capability.
327
+ <code>tool-search</code> はオンデマンドでフル階層のツールを検出しますが、
328
+ 明示的にフル階層を選択しない限り、実行にはコネクタ カタログまたは
329
+ 認証済み読み取りポリシーによる許可が必要です。
329
330
  </p>
330
331
  ```
331
332
 
@@ -363,15 +364,47 @@ MCP サーバーは、ホスト型コネクタ (ChatGPT、Claude)、コード
363
364
 
364
365
  ### コンパクト / コネクタ層 (デフォルト) {#connector-tier}
365
366
 
366
- デフォルトでは、接続されているすべてのエージェントに、厳選された小規模なカタログが表示されます (ツールが最大 20 ~ 30 個であるのに対し、全画面では最大 105 個)
367
+ デフォルトでは、接続されているすべてのエージェントに、厳選された小規模なカタログが表示されます ( 20 ~ 30 個に対し、フル サーフェスでは約 105 個)。アプリは明示的な `connectorCatalog` を管理するか、認証済み読み取りポリシーを有効にできます。
367
368
 
368
369
  - **テンプレートで宣言されたアプリ actions** — 安全なアプリレベルの許可リスト。プランの場合は、`create-visual-plan`、`get-visual-plan`、`share-resource`、`navigate`、`tool-search` などです。
369
370
  - **組み込みクロスアプリ ツール** — `list_apps`、`open_app`、`ask_app`、`create_embed_session`。
370
- - **`tool-search`** は常に存在するため、リストの外にあるものはすべてオンデマンドでアクセス可能です (以下を参照)。
371
+ - **`tool-search`** は検出用として常に存在します。検出されたアクションを `tools/call` で実行するには、コネクタ カタログ、認証済み読み取りポリシー、または明示的なフル カタログのオプトインによる許可が必要です。
371
372
 
372
- リスト外のツール (`db-exec`、`seed-*`、拡張スイート、ブラウザ セッション ツール、コンテキスト Xray ツールなど) はアドバタイズされず、呼び出し元が完全なカタログを選択していない限り、それらへの呼び出しは「不明なツール」として拒否されます。これにより、接続されている各エージェントのコンテキスト ウィンドウが小さく保たれ、シングル テナントのローカル開発でのみ安全なフットガンが削除されます。コネクタ層は、**テンプレートが `connectorCatalog` を宣言するときは常に**、環境変数の背後でゲートされません。
373
+ リスト外のツール (`db-exec`、`seed-*`、拡張スイート、ブラウザ セッション ツール、コンテキスト Xray ツールなど) はアドバタイズされず、呼び出し元が完全なカタログを選択していない限り、それらへの呼び出しは「不明なツール」として拒否されます。これにより、接続されている各エージェントのコンテキスト ウィンドウが小さく保たれ、シングル テナントのローカル開発でのみ安全なフットガンが削除されます。
373
374
 
374
- `tool-search` は 2 つの方法で動作します。ツール名の完全なメニューと 1 行の説明 (安価、スキーマなし) を取得する **クエリなし** で呼び出すか、パラメータの概要を使用したランク付けされた一致のクエリを使用して呼び出します。このようにして、圧縮されたクライアントは、必要なときにフルサーフェス ツールを検出してロードします。
375
+ `tool-search` は 2 つの方法で動作します。ツール名の完全なメニューと 1 行の説明 (安価、スキーマなし) を取得する **クエリなし** で呼び出すか、パラメータの概要を使用したランク付けされた一致のクエリを使用して呼び出します。コンパクト化されたクライアントが機能を検出するためのものであり、アプリ エージェントのより広い推論や書き込みが必要な処理には `ask_app` を使用します。
376
+
377
+ #### 認証済み読み取りをデフォルトで有効にする
378
+
379
+ 長い許可リストを管理せずに直接ツールを利用したいアプリは、認証済み読み取りの自動公開を有効にできます。
380
+
381
+ ```ts
382
+ export default createAgentChatPlugin({
383
+ appId: "analytics",
384
+ externalAgents: {
385
+ authenticatedReads: "auto",
386
+ writes: "ask_app_only",
387
+ // 特に機密性の高い読み取りを拒否する、任意の多層防御。
388
+ denyActions: ["get-sensitive-export"],
389
+ },
390
+ });
391
+ ```
392
+
393
+ `authenticatedReads: "auto"` が追加するのは、次のすべてを明示的に宣言したアクションだけです。
394
+
395
+ - `http: { method: "GET" }`
396
+ - `readOnly: true`
397
+ - `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`
398
+
399
+ このポリシーは、それらのアクションを明示的な `connectorCatalog` エントリと結合してから `denyActions` を適用します。`writes: "ask_app_only"` (自動読み取りが有効な場合のデフォルト) では、変更を伴うツールを直接呼び出せません。複数ステップの処理や変更は `ask_app` 経由で行います。`writes: "allowlisted"` は、明示的な書き込み許可リストを意図的に管理するアプリ専用です。
400
+
401
+ このポリシーは認証済みアクセス向けであり、匿名アクセス向けではありません。MCP OAuth/接続の ID は引き続き `runWithRequestContext` を通じて伝播するため、アクション レベルのアクセス チェック、所有者/組織のスコープ、OAuth の読み取りスコープが適用されます。公開/未認証アクションは自動読み取りに含まれません。
402
+
403
+ Core の `db-schema` と `db-query` は外部の自動読み取りとして公開されません。通常のスコープ付き SQL パスを通じてアプリ内エージェントからは利用できますが、広範なスキーマ/SQL アクセスは read-only メタデータだけから推測するには強力すぎます。外部クエリを直接許可する場合は、アクセスチェックと上限を持つアプリ所有の GET action、または行数・バイト数・タイムアウト・監査の制限を備えた明示的なテーブル/カラム許可リストを追加してください。`db-exec` と `db-patch` は自動公開の対象外です。
404
+
405
+ これは名前ベースの厳格な除外であり、単なるメタデータの記載漏れではありません。汎用のデータベース、seed、ブラウザ セッション、拡張、コンテキスト Xray のツールは、将来の変更で誤って完全な認証済み読み取りフラグセットが付与された場合でも、自動的に公開されることは決してありません — これらには常に明示的な `connectorCatalog` エントリが必要です。
406
+
407
+ 検証済みの Slack DM は、エージェントを実行する前に既存の Agent Native 組織メンバーと照合され、ワークスペース/ユーザーの ID リンクとして保存されます。生成されたユーザー/組織コンテキストには、そのユーザーのリソース、指示、スキルが読み込まれます。プロフィールの取得には成功したものの、メールアドレスがない、またはまだ組織メンバーではないワークスペース メンバーは、代わりに組織スコープの匿名サービスプリンシパルとして実行されます。これは共有チャンネルと同じ組織全体の可視性レベルであり、エージェントに表示される注記と、個人アクセスの取得方法を説明する 1 回限りの Slack 通知が付きます。プロフィール取得の失敗、ゲストや外部 (Slack Connect) メンバー、組織に接続されていないワークスペースには、メッセージを黙って破棄せず、丁寧な拒否応答を返します。Slack の共有チャンネルはサービスプリンシパルを使い、参加者の個人的な権限を借用しません。管理対象 OAuth と生成される Slack アプリ マニフェストは、bot スコープ `users:read.email` を要求します。新たに追加されたスコープを付与するには、既存のインストールを再接続/再インストールする必要があります。従来の bot トークンによるインストールでは、Slack でスコープを手動追加してください。このスコープがない場合、DM は個人アクセスではなく組織スコープの匿名レベルで実行されます。
375
408
 
376
409
  ### フルティア (明示的なオプトインのみ) {#full-tier}
377
410
 
@@ -473,7 +506,7 @@ ChatGPT/Claude スタイルの OAuth アプリ ホストの場合、検出サー
473
506
 
474
507
  `create_workspace_app` は、許可リストに登録されていないテンプレートを拒否します。`packages/shared-app-config/templates.ts` のパブリック テンプレートの許可リストは権限があり、CI で保護されています。外部エージェントがそれを広げることはできません。同じ名前のテンプレート アクションは、組み込みをオーバーライドします (コアよりテンプレートが優先されます)。 `MCPConfig.builtinCrossAppTools: false` でセット全体を無効にします。
475
508
 
476
- アプリ ホストのツール カタログとリソース カタログは、デフォルトではコンパクトです。[Catalog tiers](#catalog-tiers) を参照してください。 `publicAgent.expose` は、コンパクト カタログ外の安全な読み取り/取り込みツールのオプトインのままです。チャットホスト検出に表示する必要がある actions のまれな例外としてのみ、`mcpApp.compactCatalog: true` を設定します。
509
+ アプリ ホストのツールおよびリソース カタログはデフォルトでコンパクトです。[カタログ階層](#catalog-tiers) を参照してください。`publicAgent.expose` は、コンパクト カタログ外の安全な読み取り/取り込みツールをアクション単位で有効にする設定です。アプリは `externalAgents.authenticatedReads: "auto"` を設定することで、手書きのカタログなしに認証済み読み取りを公開できます。`mcpApp.compactCatalog: true` は、チャット ホストの検出に必ず表示する必要があるアクションのまれな例外としてのみ設定してください。
477
510
 
478
511
  高速 ChatGPT/Claude ハンドオフの場合、理想的なパスは直接パスです。アーティファクトを作成または開くアクションを呼び出してから、MCP アプリにルートを起動させます。 Mail リクエストは `manage_draft` を呼び出し、実際の作成ルートをレンダリングする必要があります。ダッシュボード リクエストでは、`open_app({ path, embed: true })` を呼び出すか、`mcpApp` を使用してダッシュボード アクションを呼び出し、完全な Analytics ルートをレンダリングする必要があります。カレンダー、フォーム、コンテンツ、スライド、デザイン、クリップは、下書き/作成/検索 actions と同じパターンに従う必要があります。 `list_apps` は、モデルが許可されたアプリの中から選択する必要がある場合に便利です。広範な `resources/list`、フルカタログ検出、または `ask_app` 委任は、明らかな UI ハンドオフの通常のルートであってはなりません。
479
512
 
@@ -324,8 +324,9 @@ MCP 서버는 호스팅된 커넥터(ChatGPT, Claude), 코드 클라이언트(Cl
324
324
  </div>
325
325
  </div>
326
326
  <p class="diagram-muted note">
327
- <code>tool-search</code> reaches any full-tier tool on demand &mdash; so the
328
- compact default keeps context small without hiding capability.
327
+ <code>tool-search</code>는 필요할 전체 계층 도구를 검색하지만, 명시적으로
328
+ 전체 계층을 선택하지 않는 실행에는 커넥터 카탈로그 또는 인증된 읽기 정책의
329
+ 허용이 필요합니다.
329
330
  </p>
330
331
  ```
331
332
 
@@ -363,15 +364,47 @@ MCP 서버는 호스팅된 커넥터(ChatGPT, Claude), 코드 클라이언트(Cl
363
364
 
364
365
  ### 컴팩트/커넥터 계층(기본값) {#connector-tier}
365
366
 
366
- 기본적으로 연결된 모든 에이전트는 작고 선별된 카탈로그를 봅니다(최대 20~30개의 도구 대 전체 표면의 경우 최대 105개).
367
+ 기본적으로 연결된 모든 에이전트는 작고 선별된 카탈로그를 봅니다( 20~30개의 도구 대 전체 표면의 105개). 앱은 명시적인 `connectorCatalog`를 관리하거나 인증된 읽기 정책을 선택할 수 있습니다.
367
368
 
368
369
  - **템플릿에서 선언된 앱 actions** — 안전한 앱 수준 허용 목록입니다. `create-visual-plan`, `get-visual-plan`, `share-resource`, `navigate`, `tool-search` 및 유사한 계획의 경우
369
370
  - **내장된 교차 앱 도구** — `list_apps`, `open_app`, `ask_app`, `create_embed_session`.
370
- - **`tool-search`**는 항상 존재하므로 목록 외부의 항목은 요청 계속 연결할 있습니다(아래 참조).
371
+ - **`tool-search`**는 검색을 위해 항상 제공됩니다. 검색된 action을 `tools/call`로 실행하려면 커넥터 카탈로그, 인증된 읽기 정책 또는 명시적인 전체 카탈로그 선택에서 허용되어야 합니다.
371
372
 
372
- 목록 외부의 도구(예: `db-exec`, `seed-*`, 확장 제품군, 브라우저 세션 도구 및 context-xray 도구)는 광고되지 않으며 호출자가 전체 카탈로그를 선택하지 않는 한 "알 수 없는 도구"와 함께 해당 호출이 거부됩니다. 이렇게 하면 연결된 각 에이전트의 컨텍스트 창을 작게 유지하고 단일 테넌트 로컬 개발에만 안전한 풋건을 제거합니다. 커넥터 계층은 **템플릿이 `connectorCatalog`를 선언할 때마다** 활성화되며 환경 변수 뒤에 제어되지 않습니다.
373
+ 목록 외부의 도구(예: `db-exec`, `seed-*`, 확장 제품군, 브라우저 세션 도구 및 context-xray 도구)는 광고되지 않으며 호출자가 전체 카탈로그를 선택하지 않는 한 "알 수 없는 도구"와 함께 해당 호출이 거부됩니다. 이렇게 하면 연결된 각 에이전트의 컨텍스트 창을 작게 유지하고 단일 테넌트 로컬 개발에만 안전한 위험한 도구를 제거합니다.
373
374
 
374
- `tool-search`는 두 가지 방식으로 작동합니다. 도구 이름의 전체 메뉴와 한 줄 설명(저렴함, 스키마 없음) 대한 **쿼리 없음**을 사용하여 호출하거나 매개변수 요약이 있는 순위 일치에 대한 쿼리를 사용하여 호출합니다. 이것이 압축된 클라이언트가 필요할 전체 표면 도구를 검색하고 로드하는 방법입니다.
375
+ `tool-search`는 두 가지 방식으로 작동합니다. 도구 이름의 전체 메뉴와 한 줄 설명(저렴함, 스키마 없음) **쿼리 없이** 호출하고, 매개변수 요약이 포함된 순위 결과는 쿼리와 함께 호출합니다. 이는 압축된 클라이언트가 기능을 찾도록 돕습니다. 에이전트의 폭넓은 추론이나 쓰기가 필요한 작업에는 `ask_app`을 사용하세요.
376
+
377
+ #### 인증된 읽기를 기본으로 활성화
378
+
379
+ 긴 허용 목록을 관리하지 않고 직접 도구가 바로 작동하도록 하려는 앱은 자동 인증 읽기를 선택할 수 있습니다.
380
+
381
+ ```ts
382
+ export default createAgentChatPlugin({
383
+ appId: "analytics",
384
+ externalAgents: {
385
+ authenticatedReads: "auto",
386
+ writes: "ask_app_only",
387
+ // 특히 민감한 읽기를 거부하기 위한 선택적 심층 방어입니다.
388
+ denyActions: ["get-sensitive-export"],
389
+ },
390
+ });
391
+ ```
392
+
393
+ `authenticatedReads: "auto"`는 다음을 모두 명시적으로 선언한 action만 추가합니다.
394
+
395
+ - `http: { method: "GET" }`
396
+ - `readOnly: true`
397
+ - `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`
398
+
399
+ 정책은 이러한 action을 명시적인 `connectorCatalog` 항목과 결합한 다음 `denyActions`를 적용합니다. `writes: "ask_app_only"`(자동 읽기가 활성화된 경우 기본값)를 사용하면 변경 도구를 직접 호출할 수 없습니다. 여러 단계의 작업과 변경은 `ask_app`을 통해 처리하세요. `writes: "allowlisted"`는 명시적인 쓰기 허용 목록을 의도적으로 관리하는 앱에만 사용합니다.
400
+
401
+ 이 정책은 인증된 액세스를 위한 것이며 익명 액세스를 위한 것이 아닙니다. MCP OAuth/연결 ID는 계속 `runWithRequestContext`를 통해 전달되므로 action 수준 액세스 검사, 소유자/조직 범위 지정 및 OAuth 읽기 범위가 그대로 적용됩니다. 공개/미인증 action은 자동 읽기에 포함되지 않습니다.
402
+
403
+ Core `db-schema`와 `db-query`는 자동 외부 읽기로 공개되지 않습니다. 일반적인 범위 지정 SQL 경로를 통해 앱 내부 에이전트에서는 사용할 수 있지만, 광범위한 스키마/SQL 접근은 읽기 전용 메타데이터만으로 추론하기에는 너무 강력합니다. 외부 쿼리가 필요하면 접근 검사와 제한을 갖춘 앱 소유 GET action 또는 행·바이트·시간 제한과 감사를 포함한 명시적 테이블/열 허용 목록을 제공하세요. `db-exec`와 `db-patch`는 자동 표면에 포함되지 않습니다.
404
+
405
+ 이는 이름 기반의 강력한 제외이며 단순한 메타데이터 누락이 아닙니다. 일반 데이터베이스, seed, 브라우저 세션, 확장, context-xray 도구는 향후 변경으로 인해 실수로 전체 인증된 읽기 플래그 집합이 지정되더라도 절대 자동으로 노출되지 않습니다 — 이러한 도구는 항상 명시적인 `connectorCatalog` 항목이 필요합니다.
406
+
407
+ 검증된 Slack DM은 에이전트가 실행되기 전에 기존 Agent Native 조직 구성원과 연결되고 workspace/user ID 링크로 저장됩니다. 그 결과 생성된 사용자/조직 컨텍스트에는 해당 사용자의 리소스, 지침 및 skills가 로드됩니다. 프로필을 정상적으로 가져왔지만 이메일이 없거나 아직 조직 구성원이 아닌 workspace 구성원은 조직 범위의 익명 service principal로 실행됩니다. 이는 공유 채널과 동일한 조직 전체 공개 수준이며, 에이전트에 표시되는 메모와 개인 액세스를 얻는 방법을 설명하는 일회성 Slack 안내가 함께 제공됩니다. 프로필 가져오기 실패, 게스트 및 외부(Slack Connect) 구성원, 조직에 연결되지 않은 workspace에는 메시지를 조용히 버리는 대신 정중한 거절 응답을 보냅니다. Slack 공유 채널은 service principal을 사용하며 참여자의 개인 권한을 빌리지 않습니다. 관리형 OAuth와 생성된 Slack 앱 매니페스트는 모두 bot scope `users:read.email`을 요청합니다. 새로 추가된 scope를 부여하려면 기존 설치를 다시 연결/설치해야 하며, 이전 bot-token 설치는 Slack에서 scope를 수동으로 추가해야 합니다. 이 scope가 없으면 DM은 개인 액세스 대신 조직 범위의 익명 수준에서 실행됩니다.
375
408
 
376
409
  ### 전체 등급(명시적 선택만 해당) {#full-tier}
377
410
 
@@ -475,7 +508,7 @@ ChatGPT/Claude 스타일 OAuth 앱 호스트의 경우 검색 표면은 기본
475
508
 
476
509
  `create_workspace_app`는 허용 목록에 없는 템플릿을 거부합니다. `packages/shared-app-config/templates.ts`의 공개 템플릿 허용 목록은 신뢰할 수 있고 CI로 보호됩니다. 외부 에이전트는 이를 확장할 수 없습니다. 동일한 이름의 템플릿 작업은 기본 제공(코어보다 템플릿 우선 순위)을 재정의합니다. `MCPConfig.builtinCrossAppTools: false`로 전체 세트를 비활성화하세요.
477
510
 
478
- 호스트용 도구 및 리소스 카탈로그는 기본적으로 컴팩트합니다. [Catalog tiers](#catalog-tiers) 참조하세요. `publicAgent.expose`는 컴팩트 카탈로그 외부의 안전한 읽기/수집 도구에 대한 선택 사항으로 남아 있습니다. `mcpApp.compactCatalog: true`를 채팅 호스트 검색에 나타나야 하는 actions에 대한 드문 예외로만 설정하세요.
511
+ 호스트의 도구 및 리소스 카탈로그는 기본적으로 컴팩트합니다. [카탈로그 계층](#catalog-tiers) 참조하세요. `publicAgent.expose`는 컴팩트 카탈로그 외부의 안전한 읽기/수집 도구를 action 단위로 선택하는 설정입니다. 앱은 `externalAgents.authenticatedReads: "auto"`를 설정하여 수동 카탈로그 없이 인증된 읽기를 노출할 수 있습니다. `mcpApp.compactCatalog: true`는 채팅 호스트 검색에 반드시 표시되어야 하는 action의 드문 예외에만 설정하세요.
479
512
 
480
513
  빠른 ChatGPT/Claude 핸드오프를 위해 이상적인 경로는 직접입니다. 아티팩트를 생성하거나 여는 작업을 호출한 다음 MCP 앱이 경로를 시작하도록 합니다. 메일 요청은 `manage_draft`를 호출하고 실제 작성 경로를 렌더링해야 합니다. 대시보드 요청은 `open_app({ path, embed: true })` 또는 `mcpApp`를 사용한 대시보드 작업을 호출하고 전체 Analytics 경로를 렌더링해야 합니다. 달력, 양식, 콘텐츠, 슬라이드, 디자인 및 클립은 초안/생성/검색 actions와 동일한 패턴을 따라야 합니다. `list_apps`는 모델이 부여된 앱 중에서 선택해야 할 때 유용합니다. 광범위한 `resources/list`, 전체 카탈로그 검색 또는 `ask_app` 위임은 명백한 UI 핸드오프를 위한 일반적인 경로가 되어서는 안 됩니다.
481
514
 
@@ -325,8 +325,10 @@ O servidor MCP fornece um **catálogo compacto por padrão para cada chamador**
325
325
  </div>
326
326
  </div>
327
327
  <p class="diagram-muted note">
328
- <code>tool-search</code> reaches any full-tier tool on demand &mdash; so the
329
- compact default keeps context small without hiding capability.
328
+ <code>tool-search</code> descobre ferramentas da camada completa sob demanda;
329
+ o catálogo do conector ou a política de leituras autenticadas ainda precisa
330
+ permitir a execução, salvo quando o chamador opta explicitamente pela camada
331
+ completa.
330
332
  </p>
331
333
  ```
332
334
 
@@ -364,15 +366,47 @@ O servidor MCP fornece um **catálogo compacto por padrão para cada chamador**
364
366
 
365
367
  ### Camada compacta/conector (padrão) {#connector-tier}
366
368
 
367
- Por padrão, cada agente conectado vê um catálogo pequeno e selecionado (cerca de 20 a 30 ferramentas versus cerca de 105 na superfície completa):
369
+ Por padrão, cada agente conectado vê um catálogo pequeno e selecionado (cerca de 20 a 30 ferramentas versus cerca de 105 na superfície completa). Os aplicativos podem manter um `connectorCatalog` explícito ou optar pela política de leituras autenticadas:
368
370
 
369
371
  - **Aplicativo declarado por modelo actions** — a lista de permissões segura no nível do aplicativo. Para planos `create-visual-plan`, `get-visual-plan`, `share-resource`, `navigate`, `tool-search` e similares.
370
372
  - **Ferramentas integradas para vários aplicativos** — `list_apps`, `open_app`, `ask_app`, `create_embed_session`.
371
- - **`tool-search`** está sempre presente, então qualquer coisa fora da lista permanece acessível sob demanda (veja abaixo).
373
+ - **`tool-search`** está sempre presente para descoberta. Uma action descoberta ainda precisa estar no catálogo do conector, ser incluída pela política de leituras autenticadas ou ser habilitada pela opção explícita de catálogo completo antes que `tools/call` possa executá-la.
372
374
 
373
- Ferramentas fora da lista — por exemplo `db-exec`, `seed-*`, o conjunto de extensões, ferramentas de sessão de navegador e ferramentas de raio X de contexto — não são anunciadas e as chamadas para elas são rejeitadas com "Ferramenta desconhecida", a menos que o chamador tenha optado pelo catálogo completo. Isso mantém pequena a janela de contexto de cada agente conectado e remove armas de fogo que são seguras apenas para desenvolvimento local de locatário único. A camada do conector fica ativa **sempre que um modelo declara um `connectorCatalog`** — ele não está protegido por uma variável de ambiente.
375
+ Ferramentas fora da lista — por exemplo `db-exec`, `seed-*`, o conjunto de extensões, ferramentas de sessão de navegador e ferramentas de raio X de contexto — não são anunciadas e as chamadas para elas são rejeitadas com "Ferramenta desconhecida", a menos que o chamador tenha optado pelo catálogo completo. Isso mantém pequena a janela de contexto de cada agente conectado e remove armas de fogo que são seguras apenas para desenvolvimento local de locatário único.
374
376
 
375
- `tool-search` funciona de duas maneiras: chame-o com **sem consulta** para obter o menu completo de nomes de ferramentas mais descrições de uma linha (barato, sem esquemas) ou com uma consulta para correspondências classificadas com resumos de parâmetros. É assim que um cliente compactado descobre e carrega qualquer ferramenta de superfície completa quando precisa.
377
+ `tool-search` funciona de duas maneiras: chame-o **sem consulta** para obter o menu completo de nomes de ferramentas mais descrições de uma linha (barato, sem esquemas) ou com uma consulta para correspondências classificadas com resumos de parâmetros. Ele ajuda um cliente compacto a descobrir recursos; use `ask_app` para qualquer tarefa que exija o raciocínio mais amplo do agente do aplicativo ou uma gravação.
378
+
379
+ #### Leituras autenticadas por padrão
380
+
381
+ Aplicativos que desejam que as ferramentas de leitura direta funcionem sem manter uma lista longa podem optar por leituras autenticadas automáticas:
382
+
383
+ ```ts
384
+ export default createAgentChatPlugin({
385
+ appId: "analytics",
386
+ externalAgents: {
387
+ authenticatedReads: "auto",
388
+ writes: "ask_app_only",
389
+ // Veto opcional de defesa em profundidade para leituras especialmente sensíveis.
390
+ denyActions: ["get-sensitive-export"],
391
+ },
392
+ });
393
+ ```
394
+
395
+ `authenticatedReads: "auto"` adiciona somente actions que declaram explicitamente todos os itens a seguir:
396
+
397
+ - `http: { method: "GET" }`
398
+ - `readOnly: true`
399
+ - `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`
400
+
401
+ A política combina essas actions com entradas explícitas de `connectorCatalog` e depois aplica `denyActions`. Com `writes: "ask_app_only"` (o padrão quando as leituras automáticas estão habilitadas), ferramentas que alteram dados não podem ser chamadas diretamente; encaminhe trabalhos de várias etapas e mutações por `ask_app`. `writes: "allowlisted"` existe apenas para aplicativos que mantêm intencionalmente uma lista explícita de gravações permitidas.
402
+
403
+ A política é autenticada, não anônima. A identidade MCP OAuth/connect é transmitida por `runWithRequestContext`, portanto as verificações de acesso das actions, os escopos de proprietário/organização e os escopos de leitura OAuth continuam valendo. Actions públicas ou não autenticadas não são incluídas automaticamente.
404
+
405
+ Os `db-schema` e `db-query` do Core não são expostos automaticamente como leituras externas. Eles continuam disponíveis ao agente interno do aplicativo pelo caminho SQL com escopo normal, mas o acesso amplo ao esquema/SQL é poderoso demais para ser inferido apenas de metadados de leitura. Se o aplicativo precisar de consultas externas diretas, exponha uma action GET própria com controles e limites de acesso, ou uma lista explícita de tabelas/colunas com limites de linhas, bytes, tempo e auditoria. `db-exec` e `db-patch` permanecem fora da superfície automática.
406
+
407
+ Trata-se de uma exclusão rígida baseada em nome, não apenas uma omissão de metadados: ferramentas genéricas de banco de dados, seed, sessão de navegador, extensões e raio X de contexto nunca são expostas automaticamente, mesmo que uma mudança futura marque acidentalmente uma delas com o conjunto completo de flags de leitura autenticada — elas sempre exigem uma entrada explícita em `connectorCatalog`.
408
+
409
+ Antes de o agente ser executado, uma DM verificada do Slack é associada a um membro existente da organização Agent Native e o vínculo de identidade entre workspace e usuário é salvo. O contexto resultante de usuário/organização carrega os recursos, as instruções e as skills desse usuário. Membros do workspace cujo perfil foi carregado, mas cujo e-mail está ausente ou que ainda não pertencem à organização, são executados como um principal de serviço anônimo com escopo da organização — o mesmo nível de visibilidade para toda a organização usado pelos canais compartilhados — com uma observação visível ao agente e um aviso único no Slack explicando como obter acesso pessoal. Falhas no carregamento do perfil, convidados e membros externos (Slack Connect), e workspaces não conectados a uma organização recebem uma resposta educada de recusa em vez de a mensagem ser descartada silenciosamente. Canais compartilhados do Slack usam um principal de serviço e não herdam as permissões privadas de um participante. O OAuth gerenciado e o manifesto gerado do aplicativo Slack solicitam o escopo de bot `users:read.email`. Instalações existentes precisam ser reconectadas/reinstaladas para conceder um escopo recém-adicionado; instalações antigas com token de bot precisam adicioná-lo manualmente no Slack. Sem ele, as DMs são executadas no nível anônimo com escopo da organização, e não com acesso pessoal.
376
410
 
377
411
  ### Nível completo (somente aceitação explícita) {#full-tier}
378
412
 
@@ -474,7 +508,7 @@ Além das ferramentas por ação, o servidor MCP expõe um conjunto de verbos es
474
508
 
475
509
  `create_workspace_app` rejeita qualquer modelo não listado na lista de permissões — a lista de permissões de modelos públicos no `packages/shared-app-config/templates.ts` é oficial e protegida por CI; um agente externo não pode ampliá-lo. Uma ação de modelo com o mesmo nome substitui uma ação interna (precedência de modelo sobre núcleo). Desative todo o conjunto com `MCPConfig.builtinCrossAppTools: false`.
476
510
 
477
- Os catálogos de ferramentas e recursos para hosts de aplicativos são compactos por padrão — consulte [Catalog tiers](#catalog-tiers). `publicAgent.expose` continua sendo a opção para ferramentas seguras de leitura/ingestão fora desse catálogo compacto; defina `mcpApp.compactCatalog: true` apenas como uma rara exceção para actions que deve aparecer na descoberta do host do chat.
511
+ Os catálogos de ferramentas e recursos para hosts de aplicativos são compactos por padrão — consulte [Catalog tiers](#catalog-tiers). `publicAgent.expose` continua sendo a opção no nível da action para ferramentas seguras de leitura/ingestão fora desse catálogo compacto; os aplicativos podem definir `externalAgents.authenticatedReads: "auto"` para anunciar essas leituras autenticadas sem um catálogo escrito à mão. Defina `mcpApp.compactCatalog: true` apenas como uma rara exceção para actions que precisam aparecer na descoberta do host do chat.
478
512
 
479
513
  Para transferências rápidas de ChatGPT/Claude, o caminho ideal é direto: chame a ação que cria ou abre o artefato e deixe o aplicativo MCP iniciar a rota. Uma solicitação de correio deve chamar `manage_draft` e renderizar a rota de composição real. Uma solicitação de painel deve chamar `open_app({ path, embed: true })` ou uma ação de painel com `mcpApp` e renderizar a rota completa do Analytics. Calendário, Formulários, Conteúdo, Slides, Design e Clipes devem seguir o mesmo padrão com seu rascunho/criação/pesquisa actions. `list_apps` é útil quando o modelo precisa escolher entre aplicativos concedidos; `resources/list` amplo, descoberta de catálogo completo ou delegação `ask_app` não devem ser o caminho normal para uma transferência óbvia de UI.
480
514
 
@@ -324,8 +324,8 @@ MCP 服务器默认为每个调用者提供一个**紧凑的目录** — 托管
324
324
  </div>
325
325
  </div>
326
326
  <p class="diagram-muted note">
327
- <code>tool-search</code> reaches any full-tier tool on demand &mdash; so the
328
- compact default keeps context small without hiding capability.
327
+ <code>tool-search</code> 可按需发现完整层工具;除非调用方明确选择完整层,
328
+ 否则连接器目录或已认证读取策略仍须允许执行。
329
329
  </p>
330
330
  ```
331
331
 
@@ -363,15 +363,47 @@ MCP 服务器默认为每个调用者提供一个**紧凑的目录** — 托管
363
363
 
364
364
  ### 紧凑/连接器层(默认) {#connector-tier}
365
365
 
366
- 默认情况下,每个连接的代理都会看到一个小型的、精心策划的目录(约 20–30 个工具,而整个表面上约 105 个工具):
366
+ 默认情况下,每个连接的代理都会看到一个小型的、精心策划的目录(约 20–30 个工具,而完整界面约有 105 个工具)。应用可以维护明确的 `connectorCatalog`,也可以选择已认证读取策略:
367
367
 
368
368
  - **模板声明的应用程序 actions** — 安全应用程序级别允许列表。对于 `create-visual-plan`、`get-visual-plan`、`share-resource`、`navigate`、`tool-search` 等类似计划。
369
369
  - **内置跨应用工具** - `list_apps`、`open_app`、`ask_app`、`create_embed_session`。
370
- - **`tool-search`** 始终存在,因此列表之外的任何内容都可以按需访问(见下文)。
370
+ - **`tool-search`** 始终用于发现。发现的 action 必须获得连接器目录、已认证读取策略或明确的完整目录选择允许,`tools/call` 才能执行它。
371
371
 
372
- 列表之外的工具(例如 `db-exec`、`seed-*`、扩展套件、浏览器会话工具和上下文 X 射线工具)不会公布,并且对它们的调用将被拒绝并显示“未知工具”,除非调用者选择加入完整目录。这使每个连接的代理的上下文窗口保持较小,并消除了仅对单租户本地开发安全的脚枪。 **每当模板声明 `connectorCatalog`** 时,连接器层就会处于活动状态 - 它不会受到环境变量的限制。
372
+ 列表之外的工具(例如 `db-exec`、`seed-*`、扩展套件、浏览器会话工具和上下文 X 射线工具)不会公布,并且对它们的调用将被拒绝并显示“未知工具”,除非调用者选择加入完整目录。这使每个连接的代理的上下文窗口保持较小,并消除了仅对单租户本地开发安全的危险工具。
373
373
 
374
- `tool-search` 有两种工作方式:使用**无查询**来调用它,以获取工具名称的完整菜单加上一行描述(便宜,无模式),或者使用参数摘要来查询排名匹配。这就是压缩客户端在需要时发现并加载任何全表面工具的方式。
374
+ `tool-search` 有两种工作方式:使用**无查询**调用它,以获取工具名称的完整菜单及单行描述(开销低、无 schema),或使用查询获取带参数摘要的排序匹配项。它帮助精简客户端发现功能;需要应用代理进行更广泛推理或写入时,请使用 `ask_app`。
375
+
376
+ #### 默认启用已认证读取
377
+
378
+ 希望直接工具无需维护长允许列表即可使用的应用,可以选择自动已认证读取:
379
+
380
+ ```ts
381
+ export default createAgentChatPlugin({
382
+ appId: "analytics",
383
+ externalAgents: {
384
+ authenticatedReads: "auto",
385
+ writes: "ask_app_only",
386
+ // 可选的纵深防御:拒绝特别敏感的读取。
387
+ denyActions: ["get-sensitive-export"],
388
+ },
389
+ });
390
+ ```
391
+
392
+ `authenticatedReads: "auto"` 只会添加明确声明了以下全部内容的 action:
393
+
394
+ - `http: { method: "GET" }`
395
+ - `readOnly: true`
396
+ - `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`
397
+
398
+ 该策略会将这些 action 与任何明确的 `connectorCatalog` 条目合并,然后应用 `denyActions`。使用 `writes: "ask_app_only"`(启用自动读取时的默认值)时,变更类工具不能直接调用;多步骤工作和变更应通过 `ask_app` 完成。`writes: "allowlisted"` 仅适用于有意维护明确写入允许列表的应用。
399
+
400
+ 该策略面向已认证访问,而非匿名访问。MCP OAuth/连接身份仍通过 `runWithRequestContext` 传递,因此 action 级访问检查、所有者/组织作用域和 OAuth 读取作用域继续生效。公开/未认证 action 不会包含在自动读取中。
401
+
402
+ Core `db-schema` 和 `db-query` 不会自动作为外部读取公开。它们仍可通过正常的作用域 SQL 路径供应用内代理使用,但广泛的架构/SQL 访问过于强大,不能仅凭只读元数据推断。如果应用需要直接外部查询,应提供带访问检查和边界的应用自有 GET action,或明确的表/列允许列表,并限制行数、字节数、超时和审计。`db-exec` 和 `db-patch` 仍不属于自动公开范围。
403
+
404
+ 这是一种基于名称的硬性排除,而不仅仅是元数据遗漏:通用的数据库、seed、浏览器会话、扩展和上下文 X 射线工具永远不会被自动公开,即使未来的更改意外地为其中某个工具标注了完整的已认证读取标志组合也是如此 — 它们始终需要在 `connectorCatalog` 中显式声明。
405
+
406
+ 经过验证的 Slack 私信会在代理运行前匹配现有的 Agent Native 组织成员,并保存工作区/用户身份关联;由此生成的用户/组织上下文会加载该用户的资源、指令和技能。对于已成功获取资料但缺少电子邮件或尚未成为组织成员的工作区成员,系统会改用组织范围的匿名服务主体运行——其组织级可见性与共享频道相同——同时向代理显示说明,并通过 Slack 一次性提示如何获得个人访问权限。如果资料获取失败、用户是访客或外部(Slack Connect)成员,或者工作区尚未连接组织,系统会礼貌回复拒绝,而不是静默丢弃消息。Slack 共享频道使用服务主体,不会借用任何参与者的私人权限。托管 OAuth 和生成的 Slack 应用 manifest 都会请求 bot scope `users:read.email`。已有安装必须重新连接/安装才能授予新增的 scope;旧版 bot token 安装则必须在 Slack 中手动添加该 scope。没有此 scope 时,私信会在组织范围的匿名层级运行,而不会获得个人访问权限。
375
407
 
376
408
  ### 完整层(仅限明确选择加入) {#full-tier}
377
409
 
@@ -473,7 +505,7 @@ CSP 和沙箱权限等安全元数据存在于资源中
473
505
 
474
506
  `create_workspace_app` 拒绝任何非允许列表模板 - `packages/shared-app-config/templates.ts` 中的公共模板允许列表是权威且受 CI 保护的;外部代理无法扩大它。同名的模板操作会覆盖内置操作(模板优先于核心优先级)。使用 `MCPConfig.builtinCrossAppTools: false` 禁用整个设置。
475
507
 
476
- 应用程序主机的工具和资源目录默认是紧凑的 - 请参阅 [Catalog tiers](#catalog-tiers)。 `publicAgent.expose` 仍然是该紧凑目录之外安全读取/摄取工具的选择;仅将 `mcpApp.compactCatalog: true` 设置为 actions 的罕见例外,必须出现在聊天主机发现中。
508
+ 应用程序主机的工具和资源目录默认是紧凑的,请参阅[目录层级](#catalog-tiers)。`publicAgent.expose` 仍是紧凑目录之外安全读取/摄取工具的 action 级选择。应用可以设置 `externalAgents.authenticatedReads: "auto"`,无需手写目录即可公布这些已认证读取。仅将 `mcpApp.compactCatalog: true` 用作必须出现在聊天主机发现中的 action 的罕见例外。
477
509
 
478
510
  对于快速 ChatGPT/Claude 切换,理想的路径是直接的:调用创建或打开工件的操作,然后让 MCP 应用程序启动路线。邮件请求应调用 `manage_draft` 并呈现真实的撰写路由。仪表板请求应调用 `open_app({ path, embed: true })` 或使用 `mcpApp` 的仪表板操作并呈现完整的 Analytics 路径。日历、表单、内容、幻灯片、设计和剪辑的草稿/创建/搜索 actions 应遵循相同的模式。当模型必须在授予的应用程序中进行选择时,`list_apps` 非常有用;广泛的 `resources/list`、全目录发现或 `ask_app` 委派不应该是明显的 UI 切换的正常途径。
479
511
 
@@ -324,8 +324,8 @@ MCP 伺服器預設為每個呼叫者提供一個**緊湊的目錄** — 託管
324
324
  </div>
325
325
  </div>
326
326
  <p class="diagram-muted note">
327
- <code>tool-search</code> reaches any full-tier tool on demand &mdash; so the
328
- compact default keeps context small without hiding capability.
327
+ <code>tool-search</code> 可按需探索完整層工具;除非呼叫端明確選擇完整層,
328
+ 否則連線器目錄或已驗證讀取政策仍須允許執行。
329
329
  </p>
330
330
  ```
331
331
 
@@ -363,15 +363,47 @@ MCP 伺服器預設為每個呼叫者提供一個**緊湊的目錄** — 託管
363
363
 
364
364
  ### 緊湊/連線器層(預設) {#connector-tier}
365
365
 
366
- 預設情況下,每個連線的代理都會看到一個小型的、精心策劃的目錄(約 20–30 個工具,而整個表面上約 105 個工具):
366
+ 預設情況下,每個連線的代理都會看到一個小型、精心規劃的目錄(約 20–30 個工具,而完整介面約有 105 個工具)。應用程式可以維護明確的 `connectorCatalog`,也可以選擇已驗證讀取政策:
367
367
 
368
368
  - **範本聲明的應用程式 actions** — 安全應用程式層級允許清單。對於 `create-visual-plan`、`get-visual-plan`、`share-resource`、`navigate`、`tool-search` 等類似計畫。
369
369
  - **內建跨應用工具** - `list_apps`、`open_app`、`ask_app`、`create_embed_session`。
370
- - **`tool-search`** 始終存在,因此清單之外的任何內容都可以按需存取(見下文)。
370
+ - **`tool-search`** 始終用於探索。探索到的 action 必須獲得連線器目錄、已驗證讀取政策或明確的完整目錄選擇允許,`tools/call` 才能執行它。
371
371
 
372
- 清單之外的工具(例如 `db-exec`、`seed-*`、擴充功能套件、瀏覽器工作階段工具和脈絡 X 射線工具)不會公布,並且對它們的呼叫將被拒絕並顯示“未知工具”,除非呼叫者選取加入完整目錄。這使每個連線的代理的脈絡視窗保持較小,並消除了僅對單租戶本機開發安全的腳槍。 **每當範本聲明 `connectorCatalog`** 時,連線器層就會處於活動狀態 - 它不會受到環境變數的限制。
372
+ 清單之外的工具(例如 `db-exec`、`seed-*`、擴充功能套件、瀏覽器工作階段工具和 context-xray 工具)不會公布,除非呼叫端選擇完整目錄,否則呼叫會以「未知工具」遭拒。這能縮小每個連線代理的脈絡視窗,並移除僅適合單一租戶本機開發的危險工具。
373
373
 
374
- `tool-search` 有兩種工作方式:使用**無查詢**來呼叫它,以取得工具名稱的完整選單加上一行描述(便宜,無模式),或者使用參數摘要來查詢排名匹配。這就是壓縮用戶端在需要時發現並載入任何全表面工具的方式。
374
+ `tool-search` 有兩種運作方式:以**無查詢**呼叫來取得工具名稱的完整選單與單行說明(成本低、無 schema),或使用查詢取得附參數摘要的排序結果。它協助精簡用戶端探索功能;需要應用程式代理進行更廣泛推理或寫入時,請使用 `ask_app`。
375
+
376
+ #### 預設啟用已驗證讀取
377
+
378
+ 希望直接工具不必維護冗長允許清單即可使用的應用程式,可以選擇自動已驗證讀取:
379
+
380
+ ```ts
381
+ export default createAgentChatPlugin({
382
+ appId: "analytics",
383
+ externalAgents: {
384
+ authenticatedReads: "auto",
385
+ writes: "ask_app_only",
386
+ // 選用的縱深防禦:拒絕特別敏感的讀取。
387
+ denyActions: ["get-sensitive-export"],
388
+ },
389
+ });
390
+ ```
391
+
392
+ `authenticatedReads: "auto"` 只會加入明確宣告以下全部內容的 action:
393
+
394
+ - `http: { method: "GET" }`
395
+ - `readOnly: true`
396
+ - `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`
397
+
398
+ 此政策會將這些 action 與任何明確的 `connectorCatalog` 項目合併,然後套用 `denyActions`。使用 `writes: "ask_app_only"`(啟用自動讀取時的預設值)時,變更類工具無法直接呼叫;多步驟工作與變更應透過 `ask_app` 完成。`writes: "allowlisted"` 僅適用於刻意維護明確寫入允許清單的應用程式。
399
+
400
+ 此政策用於已驗證存取,而非匿名存取。MCP OAuth/連線身分仍透過 `runWithRequestContext` 傳遞,因此 action 層級存取檢查、擁有者/組織範圍及 OAuth 讀取範圍仍會生效。公開/未驗證 action 不會包含在自動讀取中。
401
+
402
+ Core `db-schema` 與 `db-query` 不會自動公開為外部讀取。它們仍可透過一般的範圍 SQL 路徑供應用程式內代理使用,但廣泛的結構描述/SQL 存取過於強大,不能只從唯讀中繼資料推斷。若應用程式需要直接外部查詢,請提供具有存取檢查與限制的應用程式自有 GET action,或明確的資料表/欄位允許清單,並限制資料列、位元組、逾時與稽核。`db-exec` 與 `db-patch` 仍不屬於自動公開範圍。
403
+
404
+ 這是一種以名稱為依據的硬性排除,而不只是中繼資料的遺漏:通用的資料庫、seed、瀏覽器工作階段、擴充功能與 context-xray 工具永遠不會自動公開,即使未來的變更意外為其中某個工具標註了完整的已驗證讀取旗標組合也一樣 — 它們一律需要在 `connectorCatalog` 中明確宣告。
405
+
406
+ 經過驗證的 Slack 私訊會在代理執行前配對現有的 Agent Native 組織成員,並儲存工作區/使用者身分連結;由此產生的使用者/組織內容會載入該使用者的資源、指示和技能。對於已成功取得資料但缺少電子郵件或尚未成為組織成員的工作區成員,系統會改以組織範圍的匿名服務主體執行——其組織級可見性與共用頻道相同——同時向代理顯示說明,並透過 Slack 一次性提示如何取得個人存取權。如果資料取得失敗、使用者是訪客或外部(Slack Connect)成員,或工作區尚未連接組織,系統會禮貌回覆拒絕,而不是靜默捨棄訊息。Slack 共用頻道使用服務主體,不會借用任何參與者的私人權限。託管 OAuth 與產生的 Slack 應用程式 manifest 都會要求 bot scope `users:read.email`。現有安裝必須重新連接/安裝,才能授予新增的 scope;舊版 bot token 安裝則必須在 Slack 中手動加入該 scope。若沒有此 scope,私訊會在組織範圍的匿名層級執行,而不會取得個人存取權。
375
407
 
376
408
  ### 完整層(僅限明確選取加入) {#full-tier}
377
409
 
@@ -473,7 +505,7 @@ CSP 和沙箱權限等安全中繼資料存在於資源中
473
505
 
474
506
  `create_workspace_app` 拒絕任何非允許清單範本 - `packages/shared-app-config/templates.ts` 中的公開範本允許清單是權威且受 CI 保護的;外部代理無法擴大它。同名的範本操作會覆蓋內建操作(範本優先於核心優先級)。使用 `MCPConfig.builtinCrossAppTools: false` 停用整個設定。
475
507
 
476
- 應用程式主機的工具和資源目錄預設是緊湊的 - 請參閱 [Catalog tiers](#catalog-tiers)。 `publicAgent.expose` 仍然是該緊湊目錄之外安全讀取/攝取工具的選取;僅將 `mcpApp.compactCatalog: true` 設定為 actions 的罕見例外,必須出現在聊天主機發現中。
508
+ 應用程式主機的工具和資源目錄預設為精簡,請參閱[目錄層級](#catalog-tiers)。`publicAgent.expose` 仍是精簡目錄之外安全讀取/擷取工具的 action 層級選項。應用程式可設定 `externalAgents.authenticatedReads: "auto"`,不必手寫目錄即可公佈這些已驗證讀取。僅將 `mcpApp.compactCatalog: true` 用於必須出現在聊天主機探索中的 action 之罕見例外。
477
509
 
478
510
  對於快速 ChatGPT/Claude 切換,理想的路徑是直接的:呼叫建立或開啟工件的操作,然後讓 MCP 應用程式啟動路由。郵件請求應呼叫 `manage_draft` 並呈現真實的撰寫路由。儀表板請求應呼叫 `open_app({ path, embed: true })` 或使用 `mcpApp` 的儀表板操作並呈現完整的 Analytics 路徑。行事曆、表單、內容、幻燈片、設計和剪輯的草稿/建立/搜尋 actions 應遵循相同的模式。當模型必須在授予的應用程式中進行選取時,`list_apps` 非常有用;廣泛的 `resources/list`、全目錄發現或 `ask_app` 委派不應該是明顯的 UI 切換的正常途徑。
479
511
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.98.5",
3
+ "version": "0.98.7",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -22,6 +22,8 @@ import {
22
22
  claimA2ATaskForProcessing,
23
23
  getA2ATaskDispatchState,
24
24
  failStuckA2ATask,
25
+ failStuckQueuedA2ATask,
26
+ settleProcessingA2ATask,
25
27
  touchQueuedA2ATaskDispatch,
26
28
  touchProcessingA2ATask,
27
29
  } from "./task-store.js";
@@ -43,6 +45,34 @@ const A2A_QUEUED_DISPATCH_STUCK_AFTER_MS = 10_000;
43
45
  const A2A_PROCESSING_STUCK_AFTER_MS = 5 * 60 * 1000;
44
46
  const A2A_PROCESSING_HEARTBEAT_MS = 30_000;
45
47
 
48
+ /**
49
+ * Hard cap on how long a task may sit in submitted/working (never reaching
50
+ * `processing`) before the dispatch-retry loop in
51
+ * `refireStuckAsyncTaskIfNeeded` gives up and fails it. Without this, a
52
+ * persistently failing dispatch (missing background function, bad A2A
53
+ * secret, 404) throttles-and-retries forever — the queued bucket otherwise
54
+ * has no terminal state. Override with A2A_QUEUED_LIFETIME_MAX_MS.
55
+ */
56
+ function a2aQueuedLifetimeMaxMs(): number {
57
+ const raw = Number(process.env.A2A_QUEUED_LIFETIME_MAX_MS);
58
+ if (Number.isFinite(raw) && raw > 0) return raw;
59
+ return 3 * 60 * 1000;
60
+ }
61
+
62
+ /**
63
+ * Hard cap on total time a task may spend in `processing`, independent of
64
+ * the liveness heartbeat. `A2A_PROCESSING_STUCK_AFTER_MS` alone only catches
65
+ * a dead process — a hung await inside a still-alive process keeps
66
+ * `updated_at` fresh via the heartbeat forever. This bounds that case
67
+ * without cutting off legitimately long runs under it. Override with
68
+ * A2A_PROCESSING_LIFETIME_MAX_MS.
69
+ */
70
+ function a2aProcessingLifetimeMaxMs(): number {
71
+ const raw = Number(process.env.A2A_PROCESSING_LIFETIME_MAX_MS);
72
+ if (Number.isFinite(raw) && raw > 0) return raw;
73
+ return 30 * 60 * 1000;
74
+ }
75
+
46
76
  /**
47
77
  * Dispatch an async A2A task to a fresh function execution. Apps that opted
48
78
  * into durable background runs reuse the emitted Netlify 15-minute worker;
@@ -52,6 +82,7 @@ async function fireProcessTaskDispatch(
52
82
  event: any,
53
83
  taskId: string,
54
84
  config: A2AConfig,
85
+ options?: { awaitResponse?: boolean },
55
86
  ): Promise<void> {
56
87
  const backgroundPath = resolveAgentChatProcessRunDispatchPath();
57
88
  const useBackgroundWorker =
@@ -70,6 +101,13 @@ async function fireProcessTaskDispatch(
70
101
  },
71
102
  }
72
103
  : {}),
104
+ // Only Netlify's background-function URL acknowledges enqueue with 202.
105
+ // The portable framework route keeps its HTTP response open until the
106
+ // processor completes, so awaiting that response would turn async
107
+ // message/send into a long synchronous request (or a 15s timeout).
108
+ ...(options?.awaitResponse && useBackgroundWorker
109
+ ? { awaitResponse: true }
110
+ : {}),
73
111
  });
74
112
  }
75
113
 
@@ -95,7 +133,7 @@ export async function processA2ATaskFromQueue(
95
133
 
96
134
  const message = claimed.history?.[0];
97
135
  if (!message) {
98
- await updateTask(taskId, {
136
+ await settleProcessingA2ATask(taskId, {
99
137
  state: "failed",
100
138
  message: {
101
139
  role: "agent",
@@ -147,7 +185,7 @@ export async function processA2ATaskFromQueue(
147
185
  );
148
186
  } catch (err: any) {
149
187
  try {
150
- await updateTask(taskId, {
188
+ await settleProcessingA2ATask(taskId, {
151
189
  state: "failed",
152
190
  message: {
153
191
  role: "agent",
@@ -367,7 +405,7 @@ async function runHandlerAndPersist(
367
405
  for await (const msg of result as AsyncGenerator<Message>) {
368
406
  lastMessage = msg;
369
407
  }
370
- await updateTask(taskId, {
408
+ await settleProcessingA2ATask(taskId, {
371
409
  state: "completed",
372
410
  message: lastMessage,
373
411
  artifacts: artifacts.length > 0 ? artifacts : undefined,
@@ -377,13 +415,13 @@ async function runHandlerAndPersist(
377
415
 
378
416
  const handlerResult = await (result as Promise<A2AHandlerResult>);
379
417
  const allArtifacts = [...artifacts, ...(handlerResult.artifacts ?? [])];
380
- await updateTask(taskId, {
418
+ await settleProcessingA2ATask(taskId, {
381
419
  state: "completed",
382
420
  message: handlerResult.message,
383
421
  artifacts: allArtifacts.length > 0 ? allArtifacts : undefined,
384
422
  });
385
423
  } catch (err: any) {
386
- await updateTask(taskId, {
424
+ await settleProcessingA2ATask(taskId, {
387
425
  state: "failed",
388
426
  message: {
389
427
  role: "agent",
@@ -483,9 +521,22 @@ async function handleSend(
483
521
  );
484
522
  const working = await updateTask(task.id, { state: "working" });
485
523
 
486
- fireProcessTaskDispatch(event, task.id, config).catch((err) => {
524
+ // Awaited, not fire-and-forget: this handler is about to return, and a
525
+ // detached dispatch fetch racing only a short settle timer can be killed
526
+ // mid-flight when the serverless response is flushed WITHOUT rejecting —
527
+ // see the `awaitResponse` doc on `fireInternalDispatch` in
528
+ // server/self-dispatch.ts. `awaitResponse: true` requests the stronger
529
+ // guarantee; `fireProcessTaskDispatch` only honors it for the Netlify
530
+ // background-worker path (fast 202 ack) and falls back to the settle
531
+ // race for the portable route, which holds its response open until the
532
+ // handler finishes.
533
+ try {
534
+ await fireProcessTaskDispatch(event, task.id, config, {
535
+ awaitResponse: true,
536
+ });
537
+ } catch (err) {
487
538
  console.error("[a2a] Failed to dispatch process-task:", err);
488
- });
539
+ }
489
540
 
490
541
  return { ...jsonRpcResult(0, working ?? task), _id: 0 };
491
542
  }
@@ -750,30 +801,54 @@ async function refireStuckAsyncTaskIfNeeded(
750
801
  if (!state.metadata?.__a2a_processor) return false;
751
802
 
752
803
  const now = Date.now();
753
- if (
754
- (state.statusState === "submitted" || state.statusState === "working") &&
755
- state.updatedAt <= now - A2A_QUEUED_DISPATCH_STUCK_AFTER_MS
756
- ) {
757
- if (await touchQueuedA2ATaskDispatch(taskId)) {
758
- await fireProcessTaskDispatch(event, taskId, config);
804
+
805
+ if (state.statusState === "submitted" || state.statusState === "working") {
806
+ const queuedLifetimeCutoff = now - a2aQueuedLifetimeMaxMs();
807
+ if (state.createdAt <= queuedLifetimeCutoff) {
808
+ // Dispatch has kept failing (or was never delivered) long enough that
809
+ // retrying further would just repeat the same failure forever — stop
810
+ // refiring and surface a terminal error instead of throttling forever.
811
+ return failStuckQueuedA2ATask(
812
+ taskId,
813
+ queuedLifetimeCutoff,
814
+ "The async A2A task could not be started because dispatch kept failing. Please retry the request.",
815
+ );
816
+ }
817
+
818
+ if (state.updatedAt <= now - A2A_QUEUED_DISPATCH_STUCK_AFTER_MS) {
819
+ if (!(await touchQueuedA2ATaskDispatch(taskId))) return false;
820
+ try {
821
+ await fireProcessTaskDispatch(event, taskId, config);
822
+ } catch (err) {
823
+ console.error(
824
+ "[a2a] Failed to refire stuck queued task dispatch:",
825
+ err,
826
+ );
827
+ return false;
828
+ }
759
829
  return true;
760
830
  }
761
831
  return false;
762
832
  }
763
833
 
764
- if (
765
- state.statusState === "processing" &&
766
- state.updatedAt <= now - A2A_PROCESSING_STUCK_AFTER_MS
767
- ) {
768
- // A processor that died mid-handler may have already performed
769
- // side-effectful work. Retrying from the top can duplicate artifacts, so
770
- // fail deterministically and let the caller issue an intentional retry.
771
- const failed = await failStuckA2ATask(
772
- taskId,
773
- now - A2A_PROCESSING_STUCK_AFTER_MS,
774
- "The async A2A processor timed out before completing. Please retry the request.",
775
- );
776
- return failed;
834
+ if (state.statusState === "processing") {
835
+ const processingStuckCutoff = now - A2A_PROCESSING_STUCK_AFTER_MS;
836
+ const processingLifetimeCutoff = now - a2aProcessingLifetimeMaxMs();
837
+ const isStale = state.updatedAt <= processingStuckCutoff;
838
+ const isOverLifetime = state.createdAt <= processingLifetimeCutoff;
839
+ if (isStale || isOverLifetime) {
840
+ // A processor that died mid-handler may have already performed
841
+ // side-effectful work. Retrying from the top can duplicate artifacts, so
842
+ // fail deterministically and let the caller issue an intentional retry.
843
+ return failStuckA2ATask(
844
+ taskId,
845
+ processingStuckCutoff,
846
+ isStale
847
+ ? "The async A2A processor timed out before completing. Please retry the request."
848
+ : "The async A2A processor exceeded its maximum run time. Please retry the request.",
849
+ processingLifetimeCutoff,
850
+ );
851
+ }
777
852
  }
778
853
 
779
854
  return false;