@agent-native/core 0.125.0 → 0.127.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +79 -0
  3. package/corpus/core/docs/content/external-agents-catalog.mdx +14 -11
  4. package/corpus/core/docs/content/mcp-protocol.mdx +9 -4
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/index.ts +2 -0
  7. package/corpus/core/src/agent/production-agent.ts +6 -1
  8. package/corpus/core/src/agent/run-manager.ts +37 -0
  9. package/corpus/core/src/agent/run-store.ts +18 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +75 -43
  11. package/corpus/core/src/client/agent-chat.ts +19 -1
  12. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -0
  13. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +23 -0
  14. package/corpus/core/src/client/settings/AgentsSection.tsx +565 -71
  15. package/corpus/core/src/client/settings/SecretsSection.tsx +71 -36
  16. package/corpus/core/src/client/use-chat-models.ts +54 -37
  17. package/corpus/core/src/localization/default-messages.ts +11 -0
  18. package/corpus/core/src/mcp/ask-app-task-handle.ts +183 -0
  19. package/corpus/core/src/mcp/builtin-tools.ts +146 -34
  20. package/corpus/core/src/server/agent-capabilities.ts +8 -1
  21. package/corpus/core/src/server/agent-chat-plugin.ts +17 -0
  22. package/corpus/core/src/server/agent-discovery.ts +1 -1
  23. package/corpus/core/src/server/agent-peer-probe.ts +133 -0
  24. package/corpus/core/src/server/core-routes-plugin.ts +67 -0
  25. package/corpus/core/src/server/email-template.ts +13 -0
  26. package/corpus/core/src/server/email.ts +40 -4
  27. package/corpus/core/src/shared/mcp-embed-headers.ts +4 -0
  28. package/corpus/core/src/sharing/actions/share-resource.ts +55 -5
  29. package/corpus/core/src/sharing/registry.ts +41 -1
  30. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
  31. package/corpus/templates/assets/actions/edit-image.ts +31 -18
  32. package/corpus/templates/assets/actions/refine-image.ts +43 -29
  33. package/corpus/templates/assets/actions/restyle-image.ts +30 -19
  34. package/corpus/templates/assets/actions/variant-slots.ts +26 -0
  35. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +85 -17
  36. package/corpus/templates/clips/changelog/2026-07-27-share-notification-emails-now-include-a-clickable-recording-.md +6 -0
  37. package/corpus/templates/clips/server/db/index.ts +28 -6
  38. package/corpus/templates/clips/server/lib/share-email-hero.ts +105 -0
  39. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +100 -12
  40. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +17 -0
  41. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +169 -22
  42. package/corpus/templates/design/app/hooks/use-question-flow.ts +21 -1
  43. package/corpus/templates/design/changelog/2026-07-28-the-model-you-pick-when-starting-a-new-design-is-now-used-fo.md +6 -0
  44. package/corpus/templates/dispatch/changelog/2026-07-28-pending-apps-are-hidden-by-default-and-app-ownership-details.md +6 -0
  45. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +5 -1
  46. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +5 -0
  47. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +31 -8
  48. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +15 -0
  49. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +78 -8
  50. package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +179 -0
  51. package/corpus/templates/slides/app/components/presentation/present-channel.ts +15 -0
  52. package/corpus/templates/slides/app/i18n/en-US.ts +10 -1
  53. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +67 -0
  54. package/corpus/templates/slides/app/lib/export-pptx-client.ts +16 -3
  55. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -0
  56. package/corpus/templates/slides/app/pages/Presentation.tsx +11 -3
  57. package/corpus/templates/slides/changelog/2026-07-28-export-to-google-slides-now-creates-the-deck-directly-in-you.md +6 -0
  58. package/corpus/templates/slides/changelog/2026-07-28-opening-a-presentation-link-directly-no-longer-bounces-back-.md +6 -0
  59. package/corpus/templates/slides/changelog/2026-07-28-presenter-view-open-a-second-window-while-presenting-to-see-.md +6 -0
  60. package/corpus/templates/slides/server/routes/api/exports/google-slides.post.ts +90 -0
  61. package/corpus/toolkit/CHANGELOG.md +6 -0
  62. package/corpus/toolkit/package.json +1 -1
  63. package/corpus/toolkit/src/composer/TiptapComposer.tsx +143 -111
  64. package/corpus/toolkit/src/composer/runtime-adapters.tsx +2 -0
  65. package/dist/a2a/index.d.ts +2 -0
  66. package/dist/a2a/index.d.ts.map +1 -1
  67. package/dist/a2a/index.js +1 -0
  68. package/dist/a2a/index.js.map +1 -1
  69. package/dist/agent/production-agent.d.ts +2 -2
  70. package/dist/agent/production-agent.d.ts.map +1 -1
  71. package/dist/agent/production-agent.js +6 -3
  72. package/dist/agent/production-agent.js.map +1 -1
  73. package/dist/agent/run-manager.d.ts +11 -0
  74. package/dist/agent/run-manager.d.ts.map +1 -1
  75. package/dist/agent/run-manager.js +34 -1
  76. package/dist/agent/run-manager.js.map +1 -1
  77. package/dist/agent/run-store.d.ts +8 -0
  78. package/dist/agent/run-store.d.ts.map +1 -1
  79. package/dist/agent/run-store.js +16 -0
  80. package/dist/agent/run-store.js.map +1 -1
  81. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  82. package/dist/client/MultiTabAssistantChat.js +60 -31
  83. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  84. package/dist/client/agent-chat.d.ts +6 -0
  85. package/dist/client/agent-chat.d.ts.map +1 -1
  86. package/dist/client/agent-chat.js +13 -1
  87. package/dist/client/agent-chat.js.map +1 -1
  88. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  89. package/dist/client/chat/tool-call-display.js +14 -0
  90. package/dist/client/chat/tool-call-display.js.map +1 -1
  91. package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
  92. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  93. package/dist/client/resources/mcp-integration-catalog.js +16 -0
  94. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  95. package/dist/client/settings/AgentsSection.d.ts.map +1 -1
  96. package/dist/client/settings/AgentsSection.js +250 -30
  97. package/dist/client/settings/AgentsSection.js.map +1 -1
  98. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  99. package/dist/client/settings/SecretsSection.js +16 -2
  100. package/dist/client/settings/SecretsSection.js.map +1 -1
  101. package/dist/client/use-chat-models.d.ts.map +1 -1
  102. package/dist/client/use-chat-models.js +46 -27
  103. package/dist/client/use-chat-models.js.map +1 -1
  104. package/dist/collab/routes.d.ts +2 -2
  105. package/dist/collab/struct-routes.d.ts +1 -1
  106. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  107. package/dist/localization/default-messages.d.ts +9 -0
  108. package/dist/localization/default-messages.d.ts.map +1 -1
  109. package/dist/localization/default-messages.js +9 -0
  110. package/dist/localization/default-messages.js.map +1 -1
  111. package/dist/mcp/ask-app-task-handle.d.ts +25 -0
  112. package/dist/mcp/ask-app-task-handle.d.ts.map +1 -0
  113. package/dist/mcp/ask-app-task-handle.js +136 -0
  114. package/dist/mcp/ask-app-task-handle.js.map +1 -0
  115. package/dist/mcp/builtin-tools.d.ts +3 -3
  116. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  117. package/dist/mcp/builtin-tools.js +115 -38
  118. package/dist/mcp/builtin-tools.js.map +1 -1
  119. package/dist/observability/routes.d.ts +1 -1
  120. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  121. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  122. package/dist/secrets/routes.d.ts +9 -9
  123. package/dist/server/agent-capabilities.d.ts +7 -1
  124. package/dist/server/agent-capabilities.d.ts.map +1 -1
  125. package/dist/server/agent-capabilities.js +1 -0
  126. package/dist/server/agent-capabilities.js.map +1 -1
  127. package/dist/server/agent-chat-plugin.d.ts +7 -0
  128. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  129. package/dist/server/agent-chat-plugin.js +16 -1
  130. package/dist/server/agent-chat-plugin.js.map +1 -1
  131. package/dist/server/agent-discovery.d.ts +1 -0
  132. package/dist/server/agent-discovery.d.ts.map +1 -1
  133. package/dist/server/agent-discovery.js +1 -1
  134. package/dist/server/agent-discovery.js.map +1 -1
  135. package/dist/server/agent-peer-probe.d.ts +37 -0
  136. package/dist/server/agent-peer-probe.d.ts.map +1 -0
  137. package/dist/server/agent-peer-probe.js +90 -0
  138. package/dist/server/agent-peer-probe.js.map +1 -0
  139. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  140. package/dist/server/core-routes-plugin.js +53 -0
  141. package/dist/server/core-routes-plugin.js.map +1 -1
  142. package/dist/server/email-template.d.ts +6 -0
  143. package/dist/server/email-template.d.ts.map +1 -1
  144. package/dist/server/email-template.js +6 -0
  145. package/dist/server/email-template.js.map +1 -1
  146. package/dist/server/email.d.ts +7 -0
  147. package/dist/server/email.d.ts.map +1 -1
  148. package/dist/server/email.js +31 -6
  149. package/dist/server/email.js.map +1 -1
  150. package/dist/shared/mcp-embed-headers.d.ts +1 -0
  151. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  152. package/dist/shared/mcp-embed-headers.js +4 -0
  153. package/dist/shared/mcp-embed-headers.js.map +1 -1
  154. package/dist/sharing/actions/share-resource.d.ts.map +1 -1
  155. package/dist/sharing/actions/share-resource.js +49 -5
  156. package/dist/sharing/actions/share-resource.js.map +1 -1
  157. package/dist/sharing/registry.d.ts +40 -1
  158. package/dist/sharing/registry.d.ts.map +1 -1
  159. package/dist/sharing/registry.js.map +1 -1
  160. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
  161. package/docs/content/external-agents-catalog.mdx +14 -11
  162. package/docs/content/mcp-protocol.mdx +9 -4
  163. package/package.json +2 -2
  164. package/src/a2a/index.ts +2 -0
  165. package/src/agent/production-agent.ts +6 -1
  166. package/src/agent/run-manager.ts +37 -0
  167. package/src/agent/run-store.ts +18 -0
  168. package/src/client/MultiTabAssistantChat.tsx +75 -43
  169. package/src/client/agent-chat.ts +19 -1
  170. package/src/client/chat/tool-call-display.tsx +25 -0
  171. package/src/client/resources/mcp-integration-catalog.ts +23 -0
  172. package/src/client/settings/AgentsSection.tsx +565 -71
  173. package/src/client/settings/SecretsSection.tsx +71 -36
  174. package/src/client/use-chat-models.ts +54 -37
  175. package/src/localization/default-messages.ts +11 -0
  176. package/src/mcp/ask-app-task-handle.ts +183 -0
  177. package/src/mcp/builtin-tools.ts +146 -34
  178. package/src/server/agent-capabilities.ts +8 -1
  179. package/src/server/agent-chat-plugin.ts +17 -0
  180. package/src/server/agent-discovery.ts +1 -1
  181. package/src/server/agent-peer-probe.ts +133 -0
  182. package/src/server/core-routes-plugin.ts +67 -0
  183. package/src/server/email-template.ts +13 -0
  184. package/src/server/email.ts +40 -4
  185. package/src/shared/mcp-embed-headers.ts +4 -0
  186. package/src/sharing/actions/share-resource.ts +55 -5
  187. package/src/sharing/registry.ts +41 -1
  188. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask-app-task-handle.js","sourceRoot":"","sources":["../../src/mcp/ask-app-task-handle.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac } from \"node:crypto\";\n\nimport { CompactEncrypt, compactDecrypt } from \"jose\";\n\nimport { getAuthSecret } from \"../server/better-auth-instance.js\";\n\nconst HANDLE_VERSION = 1;\nconst HANDLE_TTL_SECONDS = 60 * 60;\nconst INVALID_HANDLE_ERROR = \"Invalid or expired ask_app task handle.\";\n\nexport interface AskAppTaskHandleRoute {\n app: string;\n origin: string;\n routedVia: \"local\" | \"a2a\";\n requestOrigin?: string;\n}\n\ninterface AskAppTaskHandleClaims {\n version: number;\n issuerApp: string;\n issuerAudience: string;\n organization: string;\n subject: string;\n route: AskAppTaskHandleRoute;\n taskId: string;\n expiresAt: number;\n}\n\nfunction normalizedIdentity(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction normalizedAudience(value: string): string | null {\n const url = validHttpUrl(value);\n if (!url || url.search || url.hash) return null;\n const pathname = url.pathname.replace(/\\/+$/, \"\");\n return `${url.origin}${pathname}`;\n}\n\nfunction rootSecrets(): string[] {\n const a2a = process.env.A2A_SECRET?.trim();\n const auth = getAuthSecret();\n return [...new Set([a2a, auth].filter((value): value is string => !!value))];\n}\n\nfunction handleKey(rootSecret: string): Uint8Array {\n return createHmac(\"sha256\", rootSecret)\n .update(\"agent-native:ask-app-task-handle:v1\")\n .digest();\n}\n\nfunction validHttpUrl(value: string): URL | null {\n try {\n const url = new URL(value);\n if (![\"http:\", \"https:\"].includes(url.protocol)) return null;\n if (url.username || url.password) return null;\n return url;\n } catch {\n return null;\n }\n}\n\nfunction validRoute(value: unknown): value is AskAppTaskHandleRoute {\n if (!value || typeof value !== \"object\") return false;\n const route = value as Partial<AskAppTaskHandleRoute>;\n if (typeof route.app !== \"string\" || !route.app.trim()) return false;\n if (route.routedVia !== \"local\" && route.routedVia !== \"a2a\") return false;\n if (typeof route.origin !== \"string\") return false;\n const endpoint = validHttpUrl(route.origin);\n if (!endpoint) return false;\n if (route.requestOrigin == null) return true;\n if (typeof route.requestOrigin !== \"string\") return false;\n const requestOrigin = validHttpUrl(route.requestOrigin);\n return requestOrigin?.origin === endpoint.origin;\n}\n\nfunction invalidHandle(): Error {\n return new Error(INVALID_HANDLE_ERROR);\n}\n\nexport async function signAskAppTaskHandle(params: {\n issuerApp: string;\n issuerAudience: string;\n organization: string;\n subject: string;\n route: AskAppTaskHandleRoute;\n taskId: string;\n expiresInSeconds?: number;\n}): Promise<string> {\n const issuerApp = normalizedIdentity(params.issuerApp);\n const issuerAudience = normalizedAudience(params.issuerAudience);\n const organization = normalizedIdentity(params.organization);\n const subject = normalizedIdentity(params.subject);\n const taskId = params.taskId.trim();\n if (\n !issuerApp ||\n !issuerAudience ||\n !subject ||\n !taskId ||\n !validRoute(params.route)\n ) {\n throw invalidHandle();\n }\n\n const claims: AskAppTaskHandleClaims = {\n version: HANDLE_VERSION,\n issuerApp,\n issuerAudience,\n organization,\n subject,\n route: {\n ...params.route,\n app: normalizedIdentity(params.route.app),\n },\n taskId,\n expiresAt:\n Math.floor(Date.now() / 1000) +\n (params.expiresInSeconds ?? HANDLE_TTL_SECONDS),\n };\n const payload = new TextEncoder().encode(JSON.stringify(claims));\n return new CompactEncrypt(payload)\n .setProtectedHeader({\n alg: \"dir\",\n enc: \"A256GCM\",\n typ: \"agent-native-ask-app-task-handle\",\n })\n .encrypt(handleKey(rootSecrets()[0]));\n}\n\nexport async function verifyAskAppTaskHandle(\n handle: string,\n expected: {\n issuerApp: string;\n issuerAudience: string;\n organization: string;\n subject: string;\n },\n): Promise<{ route: AskAppTaskHandleRoute; taskId: string }> {\n const issuerApp = normalizedIdentity(expected.issuerApp);\n const issuerAudience = normalizedAudience(expected.issuerAudience);\n const organization = normalizedIdentity(expected.organization);\n const subject = normalizedIdentity(expected.subject);\n if (!handle.trim() || !issuerApp || !issuerAudience || !subject) {\n throw invalidHandle();\n }\n\n for (const rootSecret of rootSecrets()) {\n try {\n const { plaintext, protectedHeader } = await compactDecrypt(\n handle,\n handleKey(rootSecret),\n );\n if (\n protectedHeader.alg !== \"dir\" ||\n protectedHeader.enc !== \"A256GCM\" ||\n protectedHeader.typ !== \"agent-native-ask-app-task-handle\"\n ) {\n continue;\n }\n const claims = JSON.parse(\n new TextDecoder().decode(plaintext),\n ) as Partial<AskAppTaskHandleClaims>;\n if (\n claims.version !== HANDLE_VERSION ||\n claims.issuerApp !== issuerApp ||\n claims.issuerAudience !== issuerAudience ||\n claims.organization !== organization ||\n claims.subject !== subject ||\n typeof claims.taskId !== \"string\" ||\n !claims.taskId ||\n typeof claims.expiresAt !== \"number\" ||\n claims.expiresAt <= Math.floor(Date.now() / 1000) ||\n !validRoute(claims.route)\n ) {\n continue;\n }\n return { route: claims.route, taskId: claims.taskId };\n } catch {\n // All handle failures are deliberately indistinguishable to callers.\n }\n }\n throw invalidHandle();\n}\n"]}
@@ -21,9 +21,9 @@
21
21
  * `open_app` / `create_workspace_app` return an **absolute** URL on the
22
22
  * *target* app's origin when it differs from this app (so a workspace link
23
23
  * lands in the right app), and a relative path for the same app / standalone.
24
- * `ask_app` routes to a *different* workspace app over A2A when possible and
25
- * reports `routedVia: "a2a"`; otherwise it answers locally
26
- * (`routedVia: "local"`) and never falsely claims cross-app delegation.
24
+ * `ask_app` routes a known *different* workspace app over A2A and reports
25
+ * `routedVia: "a2a"`; unknown cross-app targets fail closed instead of running
26
+ * on the current app. Same-app requests report `routedVia: "local"`.
27
27
  * | `list_templates` | none | `{ templates: [...] }` (allow-list only) |
28
28
  *
29
29
  * Node-only at call time (workspace resolution + scaffolding use `fs`), but
@@ -1 +1 @@
1
- {"version":3,"file":"builtin-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAKhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AA6DnD,KAAK,iBAAiB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA22ChE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAU7B"}
1
+ {"version":3,"file":"builtin-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/builtin-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAahE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AA6DnD,KAAK,iBAAiB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAm9ChE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAU7B"}
@@ -21,9 +21,9 @@
21
21
  * `open_app` / `create_workspace_app` return an **absolute** URL on the
22
22
  * *target* app's origin when it differs from this app (so a workspace link
23
23
  * lands in the right app), and a relative path for the same app / standalone.
24
- * `ask_app` routes to a *different* workspace app over A2A when possible and
25
- * reports `routedVia: "a2a"`; otherwise it answers locally
26
- * (`routedVia: "local"`) and never falsely claims cross-app delegation.
24
+ * `ask_app` routes a known *different* workspace app over A2A and reports
25
+ * `routedVia: "a2a"`; unknown cross-app targets fail closed instead of running
26
+ * on the current app. Same-app requests report `routedVia: "local"`.
27
27
  * | `list_templates` | none | `{ templates: [...] }` (allow-list only) |
28
28
  *
29
29
  * Node-only at call time (workspace resolution + scaffolding use `fs`), but
@@ -32,7 +32,9 @@
32
32
  */
33
33
  import { getConfiguredAppBasePath } from "../server/app-base-path.js";
34
34
  import { buildDeepLink } from "../server/deep-link.js";
35
+ import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
35
36
  import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
37
+ import { signAskAppTaskHandle, verifyAskAppTaskHandle, } from "./ask-app-task-handle.js";
36
38
  import { embedApp } from "./embed-app.js";
37
39
  import { fetchOrgApps } from "./org-directory.js";
38
40
  /**
@@ -145,6 +147,26 @@ function selfA2AEndpointUrl(requestMeta) {
145
147
  const basePath = requestMeta?.basePath || getConfiguredAppBasePath();
146
148
  return agentNativeA2AEndpoint(`${origin}${basePath}`);
147
149
  }
150
+ function askAppIssuerAudience(requestMeta) {
151
+ const origin = requestMeta?.origin?.replace(/\/+$/, "");
152
+ if (!origin)
153
+ return null;
154
+ try {
155
+ const originUrl = new URL(origin);
156
+ if (!["http:", "https:"].includes(originUrl.protocol) ||
157
+ originUrl.username ||
158
+ originUrl.password) {
159
+ return null;
160
+ }
161
+ const basePath = requestMeta?.basePath || getConfiguredAppBasePath();
162
+ const audience = new URL(basePath || "/", `${originUrl.origin}/`);
163
+ const pathname = audience.pathname.replace(/\/+$/, "");
164
+ return `${audience.origin}${pathname}`;
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
148
170
  function boundedAskAppWaitMs(raw) {
149
171
  if (raw == null || raw === "")
150
172
  return ASK_APP_DEFAULT_INLINE_WAIT_MS;
@@ -169,13 +191,14 @@ function taskText(task) {
169
191
  .join("\n")
170
192
  .trim() ?? "");
171
193
  }
172
- function askAppTaskResult(route, task) {
194
+ function askAppTaskResult(route, task, taskHandle) {
173
195
  const status = taskState(task);
174
196
  const response = taskText(task);
175
197
  const base = {
176
198
  app: route.app,
177
199
  routedVia: route.routedVia,
178
200
  taskId: task.id,
201
+ ...(taskHandle ? { taskHandle } : {}),
179
202
  status,
180
203
  ...(route.note ? { note: route.note } : {}),
181
204
  };
@@ -206,10 +229,16 @@ function askAppTaskResult(route, task) {
206
229
  pollAfterMs: ASK_APP_POLL_INTERVAL_MS,
207
230
  poll: {
208
231
  tool: "ask_app_status",
209
- arguments: { app: route.app, taskId: task.id },
232
+ arguments: {
233
+ app: route.app,
234
+ taskId: task.id,
235
+ ...(taskHandle ? { taskHandle } : {}),
236
+ },
210
237
  },
211
238
  message: `ask_app is still ${status}. Call ask_app_status with ` +
212
- `taskId "${task.id}" to retrieve the final response.`,
239
+ (taskHandle
240
+ ? "the returned taskHandle to retrieve the final response."
241
+ : `taskId "${task.id}" to retrieve the final response.`),
213
242
  };
214
243
  }
215
244
  /**
@@ -311,7 +340,7 @@ async function waitForA2ATask(client, initialTask, deadline) {
311
340
  }
312
341
  return current;
313
342
  }
314
- async function submitAskAppA2ATask(route, message, maxWaitMs, approvedActions) {
343
+ async function submitAskAppA2ATask(route, issuerApp, issuerAudience, message, maxWaitMs, approvedActions) {
315
344
  const deadline = maxWaitMs > 0 ? Date.now() + maxWaitMs : undefined;
316
345
  const { client, metadata } = await createA2AClientForAskApp(route.origin, route.requestOrigin, deadline);
317
346
  const task = await client.send({
@@ -323,16 +352,25 @@ async function submitAskAppA2ATask(route, message, maxWaitMs, approvedActions) {
323
352
  ...(approvedActions?.length ? { approvedActions } : {}),
324
353
  });
325
354
  const finalOrRunning = await waitForA2ATask(client, task, deadline);
326
- return askAppTaskResult(route, finalOrRunning);
355
+ const subject = typeof metadata.userEmail === "string" ? metadata.userEmail : "anonymous";
356
+ const taskHandle = await signAskAppTaskHandle({
357
+ issuerApp,
358
+ issuerAudience,
359
+ organization: getRequestOrgId() ?? "",
360
+ subject,
361
+ route,
362
+ taskId: task.id,
363
+ });
364
+ return askAppTaskResult(route, finalOrRunning, taskHandle);
327
365
  }
328
- async function fetchAskAppA2ATask(route, taskId) {
366
+ async function fetchAskAppA2ATask(route, taskId, taskHandle) {
329
367
  const { client } = await createA2AClientForAskApp(route.origin);
330
368
  const maxAttempts = ASK_APP_STATUS_RETRY_DELAYS_MS.length + 1;
331
369
  for (let attempt = 0; attempt <= ASK_APP_STATUS_RETRY_DELAYS_MS.length; attempt++) {
332
370
  const startedAt = Date.now();
333
371
  try {
334
372
  const task = await client.getTask(taskId);
335
- return askAppTaskResult(route, task);
373
+ return askAppTaskResult(route, task, taskHandle);
336
374
  }
337
375
  catch (err) {
338
376
  const delayMs = ASK_APP_STATUS_RETRY_DELAYS_MS[attempt];
@@ -358,7 +396,7 @@ async function fetchAskAppA2ATask(route, taskId) {
358
396
  throw err;
359
397
  }
360
398
  if (delayMs == null) {
361
- return askAppStatusReadUnavailableResult(route, taskId, errorCategory, maxAttempts);
399
+ return askAppStatusReadUnavailableResult(route, taskId, errorCategory, maxAttempts, taskHandle);
362
400
  }
363
401
  await new Promise((resolve) => setTimeout(resolve, delayMs));
364
402
  }
@@ -405,11 +443,12 @@ function askAppStatusOriginHost(origin) {
405
443
  return "unknown";
406
444
  }
407
445
  }
408
- function askAppStatusReadUnavailableResult(route, taskId, errorCategory, attempts) {
446
+ function askAppStatusReadUnavailableResult(route, taskId, errorCategory, attempts, taskHandle) {
409
447
  return {
410
448
  app: route.app,
411
449
  routedVia: route.routedVia,
412
450
  taskId,
451
+ ...(taskHandle ? { taskHandle } : {}),
413
452
  status: "unknown",
414
453
  statusRead: "unavailable",
415
454
  retryable: true,
@@ -418,11 +457,17 @@ function askAppStatusReadUnavailableResult(route, taskId, errorCategory, attempt
418
457
  pollAfterMs: ASK_APP_POLL_INTERVAL_MS,
419
458
  poll: {
420
459
  tool: "ask_app_status",
421
- arguments: { app: route.app, taskId },
460
+ arguments: {
461
+ app: route.app,
462
+ taskId,
463
+ ...(taskHandle ? { taskHandle } : {}),
464
+ },
422
465
  },
423
466
  message: "The durable ask_app task status could not be read after bounded retries. " +
424
467
  "The task may still be running or completed. Retry ask_app_status " +
425
- "with the same app and taskId; do not resubmit ask_app.",
468
+ (taskHandle
469
+ ? "with the same taskHandle; do not resubmit ask_app."
470
+ : "with the same app and taskId; do not resubmit ask_app."),
426
471
  };
427
472
  }
428
473
  /**
@@ -487,10 +532,13 @@ function listAppsTool(config, requestMeta) {
487
532
  livePort = 0;
488
533
  }
489
534
  }
490
- const selfId = (config.appId ?? "").toLowerCase();
535
+ const selfId = currentAppId(config);
491
536
  const isSelf = (id) => !!liveOrigin &&
492
537
  (!ws.isWorkspace || (!!selfId && id.toLowerCase() === selfId));
493
- const apps = ws.apps.map((a) => isSelf(a.id)
538
+ const resolvedApps = ws.isWorkspace
539
+ ? ws.apps
540
+ : ws.apps.map((app) => ({ ...app, id: selfId, name: config.name }));
541
+ const apps = resolvedApps.map((a) => isSelf(a.id)
494
542
  ? {
495
543
  id: a.id,
496
544
  url: liveOrigin,
@@ -778,12 +826,15 @@ function createEmbedSessionTool(requestMeta) {
778
826
  */
779
827
  async function routeAskOverA2A(origin, id, message, options) {
780
828
  if (options?.durable) {
829
+ if (!options.issuerApp || !options.issuerAudience) {
830
+ throw new Error("ask_app durable routing requires an issuer app id and audience.");
831
+ }
781
832
  return submitAskAppA2ATask({
782
833
  app: id,
783
834
  origin: agentNativeA2AEndpoint(origin),
784
835
  routedVia: "a2a",
785
836
  requestOrigin: options.requestOrigin ?? origin,
786
- }, message, options.maxWaitMs ?? ASK_APP_DEFAULT_INLINE_WAIT_MS, options.approvedActions);
837
+ }, options.issuerApp, options.issuerAudience, message, options.maxWaitMs ?? ASK_APP_DEFAULT_INLINE_WAIT_MS, options.approvedActions);
787
838
  }
788
839
  const { callAgent } = await import("../a2a/client.js");
789
840
  const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
@@ -854,8 +905,8 @@ function askAppTool(config, requestMeta) {
854
905
  "param is optional (defaults to this app). When 'app' names a " +
855
906
  "different workspace app it is routed there over A2A; the result's " +
856
907
  "'routedVia' field reports whether it ran cross-app or locally. " +
857
- "On hosted MCP, long tasks may return a durable taskId instead of a " +
858
- "final response; call ask_app_status with that taskId until completed.", {
908
+ "On hosted MCP, long tasks may return a durable taskHandle instead of " +
909
+ "a final response; pass that handle to ask_app_status until completed.", {
859
910
  app: {
860
911
  type: "string",
861
912
  description: "App id to route to (optional in a single-app project)",
@@ -866,11 +917,11 @@ function askAppTool(config, requestMeta) {
866
917
  },
867
918
  async: {
868
919
  type: "boolean",
869
- description: "When true, start a durable task and return immediately with a taskId.",
920
+ description: "When true, start a durable task and return immediately with a taskHandle and legacy taskId.",
870
921
  },
871
922
  maxWaitMs: {
872
923
  type: "number",
873
- description: "Maximum time to wait inline before returning a taskId. Hosted MCP clamps this to 25000ms.",
924
+ description: "Maximum time to wait inline before returning a taskHandle. Hosted MCP clamps this to 25000ms.",
874
925
  },
875
926
  approvedActions: {
876
927
  type: "array",
@@ -892,6 +943,10 @@ function askAppTool(config, requestMeta) {
892
943
  const requestedApp = String(args.app ?? "").trim();
893
944
  const selfId = currentAppId(config);
894
945
  const useDurableA2A = Boolean(requestMeta?.origin);
946
+ const issuerAudience = askAppIssuerAudience(requestMeta);
947
+ if (useDurableA2A && !issuerAudience) {
948
+ throw new Error("ask_app durable routing requires a valid HTTP(S) issuer audience.");
949
+ }
895
950
  const maxWaitMs = isExplicitAsyncAsk(args.async)
896
951
  ? 0
897
952
  : boundedAskAppWaitMs(args.maxWaitMs);
@@ -907,6 +962,8 @@ function askAppTool(config, requestMeta) {
907
962
  try {
908
963
  return await routeAskOverA2A(targetApp.origin, targetApp.id, message, {
909
964
  durable: useDurableA2A,
965
+ issuerApp: selfId,
966
+ issuerAudience: issuerAudience ?? undefined,
910
967
  maxWaitMs,
911
968
  requestOrigin: targetApp.origin,
912
969
  approvedActions,
@@ -931,6 +988,8 @@ function askAppTool(config, requestMeta) {
931
988
  try {
932
989
  return await routeAskOverA2A(dirMatch.a2aUrl, dirMatch.id, message, {
933
990
  durable: useDurableA2A,
991
+ issuerApp: selfId,
992
+ issuerAudience: issuerAudience ?? undefined,
934
993
  maxWaitMs,
935
994
  requestOrigin: dirMatch.url,
936
995
  approvedActions,
@@ -942,19 +1001,16 @@ function askAppTool(config, requestMeta) {
942
1001
  }
943
1002
  }
944
1003
  }
945
- // Same app (or no workspace / unknown target): answer locally with this
1004
+ if (requestedApp && requestedApp.toLowerCase() !== selfId) {
1005
+ throw new Error(`No reachable ask_app route for app "${requestedApp}". ` +
1006
+ "Call list_apps and retry with an available app id.");
1007
+ }
1008
+ // Same app (or no target): answer locally with this
946
1009
  // app's own ask-agent handler — the same entry point the HTTP MCP mount
947
1010
  // + A2A use, so there is no second agent runner.
948
1011
  if (!config.askAgent) {
949
1012
  throw new Error("This app does not expose an agent (no ask-agent handler).");
950
1013
  }
951
- // If the caller named an app we couldn't route to (unknown id, or no
952
- // workspace), say so honestly instead of claiming we reached it.
953
- const unresolved = !!requestedApp && requestedApp.toLowerCase() !== selfId;
954
- const note = unresolved
955
- ? `Requested app "${requestedApp}" is not a reachable workspace ` +
956
- `app; answered with this app ("${selfId}") instead.`
957
- : undefined;
958
1014
  // Hosted MCP cannot safely keep a JSON request/response open for a full
959
1015
  // agent loop: serverless gateways can return an inactivity 504 before
960
1016
  // the result body exists. When we know the running app origin, submit the
@@ -967,8 +1023,7 @@ function askAppTool(config, requestMeta) {
967
1023
  origin: localA2AEndpointUrl,
968
1024
  routedVia: "local",
969
1025
  requestOrigin: requestMeta?.origin,
970
- ...(note ? { note } : {}),
971
- }, message, maxWaitMs, approvedActions);
1026
+ }, selfId, issuerAudience ?? "", message, maxWaitMs, approvedActions);
972
1027
  }
973
1028
  // No derivable app origin at all (e.g. stdio / no request context) —
974
1029
  // there is no `/_agent-native/a2a` endpoint to submit a durable task
@@ -984,7 +1039,6 @@ function askAppTool(config, requestMeta) {
984
1039
  return {
985
1040
  app: selfId,
986
1041
  routedVia: "local",
987
- ...(note ? { note } : {}),
988
1042
  response: inline.response || "(no response)",
989
1043
  };
990
1044
  }
@@ -993,7 +1047,7 @@ function askAppTool(config, requestMeta) {
993
1047
  // same way the old unbounded `await config.askAgent(message)` did.
994
1048
  throw new Error(inline.error || "ask_app task failed.");
995
1049
  }
996
- return askAppInlineTaskResult(selfId, inline.taskId, inline, note);
1050
+ return askAppInlineTaskResult(selfId, inline.taskId, inline);
997
1051
  },
998
1052
  };
999
1053
  }
@@ -1010,15 +1064,38 @@ function askAppStatusTool(config, requestMeta) {
1010
1064
  },
1011
1065
  taskId: {
1012
1066
  type: "string",
1013
- description: "The durable task id returned by ask_app.",
1067
+ description: "Legacy durable task id returned by ask_app. Use taskHandle when present.",
1014
1068
  },
1015
- }, ["taskId"]),
1069
+ taskHandle: {
1070
+ type: "string",
1071
+ description: "Opaque task handle returned by ask_app. It preserves the original route across cold starts and discovery changes.",
1072
+ },
1073
+ }),
1016
1074
  readOnly: true,
1017
1075
  parallelSafe: true,
1018
1076
  run: async (args) => {
1019
- const taskId = String(args.taskId ?? "").trim();
1020
- if (!taskId)
1021
- throw new Error("ask_app_status requires 'taskId'.");
1077
+ const suppliedTaskId = String(args.taskId ?? "").trim();
1078
+ const taskHandle = String(args.taskHandle ?? "").trim();
1079
+ if (!taskHandle && !suppliedTaskId) {
1080
+ throw new Error("ask_app_status requires 'taskHandle' or 'taskId'.");
1081
+ }
1082
+ if (taskHandle) {
1083
+ const verified = await verifyAskAppTaskHandle(taskHandle, {
1084
+ issuerApp: currentAppId(config),
1085
+ issuerAudience: askAppIssuerAudience(requestMeta) ?? "",
1086
+ organization: getRequestOrgId() ?? "",
1087
+ subject: getRequestUserEmail() ?? "anonymous",
1088
+ });
1089
+ const suppliedApp = String(args.app ?? "")
1090
+ .trim()
1091
+ .toLowerCase();
1092
+ if ((suppliedTaskId && suppliedTaskId !== verified.taskId) ||
1093
+ (suppliedApp && suppliedApp !== verified.route.app.toLowerCase())) {
1094
+ throw new Error("Invalid or expired ask_app task handle.");
1095
+ }
1096
+ return fetchAskAppA2ATask(verified.route, verified.taskId, taskHandle);
1097
+ }
1098
+ const taskId = suppliedTaskId;
1022
1099
  // The no-derivable-origin ask_app fallback tracks its tasks in a
1023
1100
  // process-local map, not the durable A2A store. Check it FIRST —
1024
1101
  // before any origin requirement — so a taskId minted by that fallback