@agent-native/core 0.149.2 → 0.149.4

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 (347) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +54 -488
  3. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +136 -0
  4. package/corpus/templates/analytics/.agents/skills/actions/references/examples.md +94 -0
  5. package/corpus/templates/analytics/.agents/skills/actions/references/provider-apis.md +95 -0
  6. package/corpus/templates/analytics/.agents/skills/create-skill/SKILL.md +9 -35
  7. package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +4 -2
  8. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +4 -5
  9. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -31
  10. package/corpus/templates/analytics/AGENTS.md +25 -32
  11. package/corpus/templates/analytics/app/global.css +1 -1
  12. package/corpus/templates/analytics/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  13. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +54 -488
  14. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +136 -0
  15. package/corpus/templates/assets/.agents/skills/actions/references/examples.md +94 -0
  16. package/corpus/templates/assets/.agents/skills/actions/references/provider-apis.md +95 -0
  17. package/corpus/templates/assets/.agents/skills/create-skill/SKILL.md +9 -35
  18. package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +4 -2
  19. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +4 -5
  20. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -31
  21. package/corpus/templates/assets/AGENTS.md +6 -9
  22. package/corpus/templates/assets/app/global.css +1 -1
  23. package/corpus/templates/assets/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  24. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +54 -488
  25. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +136 -0
  26. package/corpus/templates/brain/.agents/skills/actions/references/examples.md +94 -0
  27. package/corpus/templates/brain/.agents/skills/actions/references/provider-apis.md +95 -0
  28. package/corpus/templates/brain/.agents/skills/create-skill/SKILL.md +9 -35
  29. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +4 -5
  30. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -31
  31. package/corpus/templates/brain/AGENTS.md +5 -12
  32. package/corpus/templates/brain/app/global.css +1 -1
  33. package/corpus/templates/brain/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  34. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +54 -488
  35. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +136 -0
  36. package/corpus/templates/calendar/.agents/skills/actions/references/examples.md +94 -0
  37. package/corpus/templates/calendar/.agents/skills/actions/references/provider-apis.md +95 -0
  38. package/corpus/templates/calendar/.agents/skills/create-skill/SKILL.md +9 -35
  39. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +4 -5
  40. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -31
  41. package/corpus/templates/calendar/AGENTS.md +8 -9
  42. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  43. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  44. package/corpus/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  45. package/corpus/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  46. package/corpus/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  47. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  48. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  49. package/corpus/templates/chat/AGENTS.md +32 -80
  50. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +54 -488
  51. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +136 -0
  52. package/corpus/templates/clips/.agents/skills/actions/references/examples.md +94 -0
  53. package/corpus/templates/clips/.agents/skills/actions/references/provider-apis.md +95 -0
  54. package/corpus/templates/clips/.agents/skills/create-skill/SKILL.md +9 -35
  55. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +4 -5
  56. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -31
  57. package/corpus/templates/clips/AGENTS.md +25 -31
  58. package/corpus/templates/content/.agents/skills/actions/SKILL.md +54 -488
  59. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +136 -0
  60. package/corpus/templates/content/.agents/skills/actions/references/examples.md +94 -0
  61. package/corpus/templates/content/.agents/skills/actions/references/provider-apis.md +95 -0
  62. package/corpus/templates/content/.agents/skills/create-skill/SKILL.md +9 -35
  63. package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +4 -2
  64. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +4 -5
  65. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -31
  66. package/corpus/templates/content/AGENTS.md +5 -9
  67. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -31
  68. package/corpus/templates/crm/AGENTS.md +7 -11
  69. package/corpus/templates/crm/app/global.css +1 -1
  70. package/corpus/templates/crm/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  71. package/corpus/templates/design/.agents/skills/actions/SKILL.md +54 -488
  72. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +136 -0
  73. package/corpus/templates/design/.agents/skills/actions/references/examples.md +94 -0
  74. package/corpus/templates/design/.agents/skills/actions/references/provider-apis.md +95 -0
  75. package/corpus/templates/design/.agents/skills/create-skill/SKILL.md +9 -35
  76. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +4 -2
  77. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +4 -5
  78. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -31
  79. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +70 -3
  80. package/corpus/templates/design/AGENTS.md +13 -21
  81. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +12 -0
  82. package/corpus/templates/design/app/components/design/LayersPanel.tsx +19 -2
  83. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +115 -7
  84. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +82 -7
  85. package/corpus/templates/design/app/components/design/design-trace.ts +120 -0
  86. package/corpus/templates/design/app/components/design/dnd-debug.ts +2 -1
  87. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +40 -26
  88. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -1
  89. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +12 -3
  90. package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +4 -0
  91. package/corpus/templates/design/app/components/design/multi-screen/types.ts +7 -0
  92. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -2
  93. package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -2
  94. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -0
  95. package/corpus/templates/design/app/i18n-data.ts +1 -0
  96. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -5
  97. package/corpus/templates/design/app/pages/DesignSystems.tsx +3 -2
  98. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +100 -13
  99. package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +1 -1
  100. package/corpus/templates/design/app/pages/design-editor/data-operations.ts +14 -0
  101. package/corpus/templates/design/changelog/2026-08-10-marquee-selecting-layers-far-down-a-tall-screen-now-selects-.md +6 -0
  102. package/corpus/templates/design/changelog/2026-08-10-the-frame-tool-draws-a-plain-frame-by-default-with-screen-av.md +6 -0
  103. package/corpus/templates/design/e2e/global-setup.ts +27 -32
  104. package/corpus/templates/design/playwright.config.ts +3 -5
  105. package/corpus/templates/design/shared/breakpoint-media.ts +9 -2
  106. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +54 -488
  107. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +136 -0
  108. package/corpus/templates/dispatch/.agents/skills/actions/references/examples.md +94 -0
  109. package/corpus/templates/dispatch/.agents/skills/actions/references/provider-apis.md +95 -0
  110. package/corpus/templates/dispatch/.agents/skills/create-skill/SKILL.md +9 -35
  111. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +4 -5
  112. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -31
  113. package/corpus/templates/dispatch/AGENTS.md +9 -16
  114. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +54 -488
  115. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +136 -0
  116. package/corpus/templates/factory/.agents/skills/actions/references/examples.md +94 -0
  117. package/corpus/templates/factory/.agents/skills/actions/references/provider-apis.md +95 -0
  118. package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +9 -35
  119. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +4 -5
  120. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -31
  121. package/corpus/templates/factory/AGENTS.md +10 -24
  122. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +54 -488
  123. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +136 -0
  124. package/corpus/templates/forms/.agents/skills/actions/references/examples.md +94 -0
  125. package/corpus/templates/forms/.agents/skills/actions/references/provider-apis.md +95 -0
  126. package/corpus/templates/forms/.agents/skills/create-skill/SKILL.md +9 -35
  127. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +4 -5
  128. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -31
  129. package/corpus/templates/forms/AGENTS.md +11 -17
  130. package/corpus/templates/forms/app/global.css +1 -0
  131. package/corpus/templates/forms/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  132. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +4 -5
  133. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -31
  134. package/corpus/templates/macros/AGENTS.md +8 -8
  135. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +54 -488
  136. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +136 -0
  137. package/corpus/templates/mail/.agents/skills/actions/references/examples.md +94 -0
  138. package/corpus/templates/mail/.agents/skills/actions/references/provider-apis.md +95 -0
  139. package/corpus/templates/mail/.agents/skills/create-skill/SKILL.md +9 -35
  140. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +4 -5
  141. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -31
  142. package/corpus/templates/mail/AGENTS.md +5 -6
  143. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +54 -488
  144. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +136 -0
  145. package/corpus/templates/plan/.agents/skills/actions/references/examples.md +94 -0
  146. package/corpus/templates/plan/.agents/skills/actions/references/provider-apis.md +95 -0
  147. package/corpus/templates/plan/.agents/skills/create-skill/SKILL.md +9 -35
  148. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +4 -5
  149. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -31
  150. package/corpus/templates/plan/AGENTS.md +5 -14
  151. package/corpus/templates/plan/changelog/2026-08-11-plan-no-longer-reloads-when-runtime-state-changes.md +6 -0
  152. package/corpus/templates/plan/vite.config.ts +5 -0
  153. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +54 -488
  154. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +136 -0
  155. package/corpus/templates/slides/.agents/skills/actions/references/examples.md +94 -0
  156. package/corpus/templates/slides/.agents/skills/actions/references/provider-apis.md +95 -0
  157. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -2
  158. package/corpus/templates/slides/.agents/skills/create-skill/SKILL.md +9 -35
  159. package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +6 -3
  160. package/corpus/templates/slides/.agents/skills/deck-management/SKILL.md +6 -1
  161. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +4 -5
  162. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +3 -2
  163. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +3 -0
  164. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +3 -3
  165. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -31
  166. package/corpus/templates/slides/AGENTS.md +22 -25
  167. package/corpus/templates/slides/actions/get-deck-reference-context.ts +2 -2
  168. package/corpus/templates/slides/actions/get-deck.ts +57 -9
  169. package/corpus/templates/slides/actions/import-file.ts +2 -1
  170. package/corpus/templates/slides/actions/patch-deck.ts +45 -2
  171. package/corpus/templates/slides/actions/update-slide.ts +1 -1
  172. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +2 -2
  173. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -23
  174. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +2 -2
  175. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +6 -6
  176. package/corpus/templates/slides/app/components/editor/AnimationsPanel.tsx +3 -3
  177. package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +4 -4
  178. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +1 -1
  179. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +428 -180
  180. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +294 -266
  181. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +7 -5
  182. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  183. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +40 -12
  184. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +6 -12
  185. package/corpus/templates/slides/app/components/editor/editor-command-model.ts +35 -0
  186. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +8 -10
  187. package/corpus/templates/slides/app/context/DeckContext.tsx +2 -1
  188. package/corpus/templates/slides/app/global.css +76 -1
  189. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  190. package/corpus/templates/slides/app/lib/create-deck-generation.ts +51 -23
  191. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +68 -5
  192. package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -6
  193. package/corpus/templates/slides/app/pages/DesignSystems.tsx +2 -2
  194. package/corpus/templates/slides/app/pages/Index.tsx +87 -74
  195. package/corpus/templates/slides/app/root.tsx +43 -0
  196. package/corpus/templates/slides/changelog/2026-08-11-faster-slides-chat-deck-reads.md +6 -0
  197. package/corpus/templates/slides/changelog/2026-08-11-slides-chat-can-read-one-slide-without-loading-the-full-deck.md +6 -0
  198. package/corpus/templates/slides/changelog/2026-08-11-slides-clearly-indicate-when-an-ai-agent-is-editing-alongsid.md +6 -0
  199. package/corpus/templates/slides/package.json +1 -0
  200. package/corpus/templates/slides/server/lib/validate-slide-animations.ts +163 -0
  201. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -3
  202. package/corpus/templates/slides/shared/api.ts +46 -15
  203. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +54 -488
  204. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +136 -0
  205. package/corpus/templates/tasks/.agents/skills/actions/references/examples.md +94 -0
  206. package/corpus/templates/tasks/.agents/skills/actions/references/provider-apis.md +95 -0
  207. package/corpus/templates/tasks/.agents/skills/agent-watchdog/SKILL.md +1 -0
  208. package/corpus/templates/tasks/.agents/skills/create-skill/SKILL.md +9 -35
  209. package/corpus/templates/tasks/.agents/skills/efficient-fable/SKILL.md +1 -0
  210. package/corpus/templates/tasks/.agents/skills/efficient-frontier/SKILL.md +1 -0
  211. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +4 -5
  212. package/corpus/templates/tasks/.agents/skills/plan-arbiter/SKILL.md +1 -0
  213. package/corpus/templates/tasks/.agents/skills/plow-ahead/SKILL.md +1 -0
  214. package/corpus/templates/tasks/.agents/skills/quick-recap/SKILL.md +1 -0
  215. package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/SKILL.md +1 -0
  216. package/corpus/templates/tasks/.agents/skills/stay-within-limits/SKILL.md +1 -0
  217. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -31
  218. package/corpus/templates/tasks/AGENTS.md +5 -20
  219. package/dist/agent/production-agent.js +376 -345
  220. package/dist/agent/run-manager.js +156 -14
  221. package/dist/agent/run-store.d.ts +11 -10
  222. package/dist/agent/run-store.js +17 -13
  223. package/dist/client/AssistantChat.d.ts +6 -2
  224. package/dist/client/AssistantChat.js +216 -131
  225. package/dist/client/active-run-state.d.ts +9 -1
  226. package/dist/client/active-run-state.js +34 -3
  227. package/dist/client/agent-chat-adapter.d.ts +8 -0
  228. package/dist/client/agent-chat-adapter.js +114 -28
  229. package/dist/client/chat/message-components.d.ts +7 -1
  230. package/dist/client/chat/message-components.js +32 -5
  231. package/dist/client/chat/run-recovery.d.ts +1 -0
  232. package/dist/client/chat/run-recovery.js +8 -0
  233. package/dist/client/chat/tool-call-display.d.ts +2 -0
  234. package/dist/client/chat/tool-call-display.js +2 -0
  235. package/dist/client/chat-first/apps-rail.js +1 -1
  236. package/dist/client/chat-first/chat-history.d.ts +1 -1
  237. package/dist/client/chat-first/chat-history.js +5 -2
  238. package/dist/client/sse-event-processor.d.ts +4 -2
  239. package/dist/client/sse-event-processor.js +36 -6
  240. package/dist/client/use-run-stuck-detection.d.ts +2 -0
  241. package/dist/client/use-run-stuck-detection.js +85 -9
  242. package/dist/collab/routes.d.ts +1 -1
  243. package/dist/deploy/build.d.ts +4 -0
  244. package/dist/deploy/build.js +5 -0
  245. package/dist/deploy/workspace-deploy.js +5 -0
  246. package/dist/integrations/adapters/slack.d.ts +0 -4
  247. package/dist/integrations/adapters/slack.js +6 -30
  248. package/dist/integrations/catalog.js +1 -1
  249. package/dist/integrations/pending-tasks-store.d.ts +0 -8
  250. package/dist/integrations/pending-tasks-store.js +0 -21
  251. package/dist/integrations/slack-manifest.d.ts +2 -2
  252. package/dist/integrations/slack-manifest.js +0 -3
  253. package/dist/notifications/routes.d.ts +3 -3
  254. package/dist/observability/routes.d.ts +3 -3
  255. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  256. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  257. package/dist/resources/handlers.d.ts +1 -1
  258. package/dist/scripts/docs/source-search.js +2 -2
  259. package/dist/secrets/routes.d.ts +9 -9
  260. package/dist/server/agent-chat/framework-prompts.js +7 -2
  261. package/dist/server/agent-chat/prompt-resources.js +2 -2
  262. package/dist/server/agent-chat/skill-frontmatter.d.ts +31 -2
  263. package/dist/server/agent-chat/skill-frontmatter.js +41 -15
  264. package/dist/server/agent-chat-plugin.js +3 -3
  265. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  266. package/dist/server/agents-bundle.d.ts +16 -19
  267. package/dist/server/agents-bundle.js +19 -35
  268. package/dist/server/better-auth-instance.d.ts +1 -0
  269. package/dist/server/better-auth-instance.js +17 -2
  270. package/dist/server/prompts/framework-core-compact.d.ts +6 -1
  271. package/dist/server/prompts/framework-core-compact.js +12 -9
  272. package/dist/server/prompts/framework-core.d.ts +6 -1
  273. package/dist/server/prompts/framework-core.js +11 -9
  274. package/dist/server/prompts/index.d.ts +1 -1
  275. package/dist/server/prompts/index.js +1 -1
  276. package/dist/server/prompts/shared-rules.d.ts +15 -11
  277. package/dist/server/prompts/shared-rules.js +17 -13
  278. package/dist/server/realtime-token.d.ts +1 -1
  279. package/dist/server/transcribe-voice.d.ts +1 -1
  280. package/dist/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  281. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  282. package/dist/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  283. package/dist/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  284. package/dist/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  285. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  286. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  287. package/dist/templates/chat/AGENTS.md +32 -80
  288. package/dist/templates/default/.agents/skills/actions/SKILL.md +54 -488
  289. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
  290. package/dist/templates/default/.agents/skills/actions/references/examples.md +94 -0
  291. package/dist/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
  292. package/dist/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
  293. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
  294. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
  295. package/dist/templates/headless/.agents/skills/actions/SKILL.md +54 -488
  296. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
  297. package/dist/templates/headless/.agents/skills/actions/references/examples.md +94 -0
  298. package/dist/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
  299. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
  300. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
  301. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
  302. package/dist/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
  303. package/dist/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
  304. package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
  305. package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
  306. package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
  307. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
  308. package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
  309. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
  310. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
  311. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
  312. package/dist/templates/workspace-core/AGENTS.md +7 -16
  313. package/docs/content/messaging.mdx +6 -7
  314. package/package.json +1 -1
  315. package/src/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  316. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  317. package/src/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  318. package/src/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  319. package/src/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  320. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  321. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  322. package/src/templates/chat/AGENTS.md +32 -80
  323. package/src/templates/default/.agents/skills/actions/SKILL.md +54 -488
  324. package/src/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
  325. package/src/templates/default/.agents/skills/actions/references/examples.md +94 -0
  326. package/src/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
  327. package/src/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
  328. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
  329. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
  330. package/src/templates/headless/.agents/skills/actions/SKILL.md +54 -488
  331. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
  332. package/src/templates/headless/.agents/skills/actions/references/examples.md +94 -0
  333. package/src/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
  334. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
  335. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
  336. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
  337. package/src/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
  338. package/src/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
  339. package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
  340. package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
  341. package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
  342. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
  343. package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
  344. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
  345. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
  346. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
  347. package/src/templates/workspace-core/AGENTS.md +7 -16
@@ -13,7 +13,7 @@ import { extractMcpToolResultImages } from "../mcp-client/index.js";
13
13
  import { isMcpToolAllowedForRequest } from "../mcp-client/visibility.js";
14
14
  import { shouldInferSentimentForTurn } from "../observability/sentiment.js";
15
15
  import { completeRun as completeProgressRun, startRun as startProgressRun, updateRunProgress, } from "../progress/registry.js";
16
- import { getFrontmatterValue, parseFrontmatter, } from "../resources/metadata.js";
16
+ import { isRuntimeVisibleScope, parseSkillFrontmatter, } from "../server/agent-chat/skill-frontmatter.js";
17
17
  import { canUseDeployCredentialFallbackForRequest, getProviderCredentialAuthFailure, readDeployCredentialEnv, } from "../server/credential-provider.js";
18
18
  import { readBody } from "../server/h3-helpers.js";
19
19
  import { getRequestRunContext, ensureRequestRunContext, getRequestContext, getRequestOrgId, getRequestUserEmail, runWithRequestContext, } from "../server/request-context.js";
@@ -1199,9 +1199,9 @@ function escapeReferenceAttribute(value) {
1199
1199
  return value.replace(/&/g, "&").replace(/"/g, """);
1200
1200
  }
1201
1201
  function isRuntimeVisibleSkillContent(content) {
1202
- const frontmatter = parseFrontmatter(content);
1203
- const scope = getFrontmatterValue(frontmatter, "scope")?.trim().toLowerCase();
1204
- return scope !== "dev";
1202
+ // Route through the shared normalizer so an unrecognized `scope:` stays
1203
+ // invisible here too. A local `!== "dev"` check silently readmits it.
1204
+ return isRuntimeVisibleScope(parseSkillFrontmatter(content).scope);
1205
1205
  }
1206
1206
  export async function resolveSkillReferenceContent(ref) {
1207
1207
  if (!ref.path && !ref.name)
@@ -4289,370 +4289,401 @@ export async function runAgentLoop(opts) {
4289
4289
  tool: toolCall.name,
4290
4290
  input: toolCall.input,
4291
4291
  });
4292
- const toolCallSchemaError = toolCallErrors.get(toolCall.id);
4293
- if (toolCallSchemaError && !toolInputNormalized) {
4294
- const result = finalizeToolErrorResult(toolInputSchemaErrorResult(toolCall.name, toolCallSchemaError.input, toolCallSchemaError.error, actionEntry?.tool.parameters));
4295
- send({
4296
- type: "tool_done",
4297
- id: toolCall.id,
4298
- tool: toolCall.name,
4299
- input: toolCall.input,
4300
- result,
4301
- isError: true,
4302
- completedSideEffect: false,
4303
- });
4304
- recordToolResult(result, true);
4305
- return {
4306
- type: "tool-result",
4307
- toolCallId: toolCall.id,
4308
- toolName: toolCall.name,
4309
- toolInput: wireToolInput,
4310
- content: result,
4311
- isError: true,
4312
- };
4313
- }
4314
- const rawToolInputError = validateRawToolInput(actionEntry, toolCall.input);
4315
- if (rawToolInputError) {
4316
- const result = finalizeToolErrorResult(toolInputSchemaErrorResult(toolCall.name, toolCall.input, rawToolInputError, actionEntry.tool.parameters));
4317
- send({
4318
- type: "tool_done",
4319
- id: toolCall.id,
4320
- tool: toolCall.name,
4321
- input: toolCall.input,
4322
- result,
4323
- isError: true,
4324
- completedSideEffect: false,
4325
- });
4326
- recordToolResult(result, true);
4327
- return {
4328
- type: "tool-result",
4329
- toolCallId: toolCall.id,
4330
- toolName: toolCall.name,
4331
- toolInput: wireToolInput,
4332
- content: result,
4333
- isError: true,
4334
- };
4335
- }
4336
- // dedupe: false opts a read-only tool out of the guard entirely — the
4337
- // cacheKey stays null so it never gets skipped-as-duplicate and never
4338
- // populates the cache (see the success handler below, which also
4339
- // leaves dedupe:false results uncached and un-cleared).
4340
- const cacheKey = actionEntry.readOnly === true && actionEntry.dedupe !== false
4341
- ? toolCallCacheKey(toolCall.name, toolCall.input)
4342
- : null;
4343
- if (cacheKey && readOnlyToolResultCache.has(cacheKey)) {
4344
- const previousResult = readOnlyToolResultCache.get(cacheKey) ?? "";
4345
- // `contextMessages` (not `messages`) is what the model actually sees
4346
- // this iteration — context-xray eviction/summarization and
4347
- // observational-memory trimming can drop the earlier result from
4348
- // view even though it's still cached here. Only strike-count the
4349
- // repeat when the model could have looked back and found it itself.
4350
- const visible = isCachedToolResultVisibleInContext(contextMessages, toolCall, previousResult);
4292
+ let toolDoneEmitted = false;
4293
+ const emitToolDone = (event) => {
4294
+ send(event);
4295
+ toolDoneEmitted = true;
4296
+ };
4297
+ // Every tool_start must have exactly one matching tool_done. Keeping the
4298
+ // whole post-start path inside this finally block turns unexpected
4299
+ // synchronous failures in validation, cache handling, or future branches
4300
+ // into an explicit tool error instead of leaving run-manager's in-flight
4301
+ // counter latched forever.
4302
+ try {
4303
+ const toolCallSchemaError = toolCallErrors.get(toolCall.id);
4304
+ if (toolCallSchemaError && !toolInputNormalized) {
4305
+ const result = finalizeToolErrorResult(toolInputSchemaErrorResult(toolCall.name, toolCallSchemaError.input, toolCallSchemaError.error, actionEntry?.tool.parameters));
4306
+ emitToolDone({
4307
+ type: "tool_done",
4308
+ id: toolCall.id,
4309
+ tool: toolCall.name,
4310
+ input: toolCall.input,
4311
+ result,
4312
+ isError: true,
4313
+ completedSideEffect: false,
4314
+ });
4315
+ recordToolResult(result, true);
4316
+ return {
4317
+ type: "tool-result",
4318
+ toolCallId: toolCall.id,
4319
+ toolName: toolCall.name,
4320
+ toolInput: wireToolInput,
4321
+ content: result,
4322
+ isError: true,
4323
+ };
4324
+ }
4325
+ const rawToolInputError = validateRawToolInput(actionEntry, toolCall.input);
4326
+ if (rawToolInputError) {
4327
+ const result = finalizeToolErrorResult(toolInputSchemaErrorResult(toolCall.name, toolCall.input, rawToolInputError, actionEntry.tool.parameters));
4328
+ emitToolDone({
4329
+ type: "tool_done",
4330
+ id: toolCall.id,
4331
+ tool: toolCall.name,
4332
+ input: toolCall.input,
4333
+ result,
4334
+ isError: true,
4335
+ completedSideEffect: false,
4336
+ });
4337
+ recordToolResult(result, true);
4338
+ return {
4339
+ type: "tool-result",
4340
+ toolCallId: toolCall.id,
4341
+ toolName: toolCall.name,
4342
+ toolInput: wireToolInput,
4343
+ content: result,
4344
+ isError: true,
4345
+ };
4346
+ }
4347
+ // dedupe: false opts a read-only tool out of the guard entirely — the
4348
+ // cacheKey stays null so it never gets skipped-as-duplicate and never
4349
+ // populates the cache (see the success handler below, which also
4350
+ // leaves dedupe:false results uncached and un-cleared).
4351
+ const cacheKey = actionEntry.readOnly === true && actionEntry.dedupe !== false
4352
+ ? toolCallCacheKey(toolCall.name, toolCall.input)
4353
+ : null;
4354
+ if (cacheKey && readOnlyToolResultCache.has(cacheKey)) {
4355
+ const previousResult = readOnlyToolResultCache.get(cacheKey) ?? "";
4356
+ // `contextMessages` (not `messages`) is what the model actually sees
4357
+ // this iteration — context-xray eviction/summarization and
4358
+ // observational-memory trimming can drop the earlier result from
4359
+ // view even though it's still cached here. Only strike-count the
4360
+ // repeat when the model could have looked back and found it itself.
4361
+ const visible = isCachedToolResultVisibleInContext(contextMessages, toolCall, previousResult);
4362
+ let result;
4363
+ if (visible) {
4364
+ const repeats = (duplicateReadOnlyToolCalls.get(cacheKey) ?? 0) + 1;
4365
+ duplicateReadOnlyToolCalls.set(cacheKey, repeats);
4366
+ result = visibleDuplicateReadOnlyToolResult(toolCall.name);
4367
+ if (repeats >= 3) {
4368
+ requestedActionStop ??= {
4369
+ message: "I stopped because the agent kept asking for the same read-only context it already had. Please send the request again if you want me to retry from a fresh turn.",
4370
+ errorCode: "duplicate_read_only_tool",
4371
+ };
4372
+ }
4373
+ }
4374
+ else {
4375
+ // The earlier result was trimmed out of the model's visible
4376
+ // context — this isn't a repetitive loop, the model legitimately
4377
+ // can't see the answer anymore. Re-serve it in full and don't
4378
+ // count a strike.
4379
+ duplicateReadOnlyToolCalls.set(cacheKey, 0);
4380
+ result = resurfacedDuplicateReadOnlyToolResult(toolCall.name, previousResult);
4381
+ }
4382
+ emitToolDone({
4383
+ type: "tool_done",
4384
+ id: toolCall.id,
4385
+ tool: toolCall.name,
4386
+ input: toolCall.input,
4387
+ result,
4388
+ completedSideEffect: false,
4389
+ });
4390
+ recordToolResult(result, false);
4391
+ return {
4392
+ type: "tool-result",
4393
+ toolCallId: toolCall.id,
4394
+ toolName: toolCall.name,
4395
+ toolInput: wireToolInput,
4396
+ content: result,
4397
+ };
4398
+ }
4399
+ if (opts.executionMode === "plan" &&
4400
+ !isPlanModeToolCallAllowed(toolCall.name, toolCall.input, actionEntry)) {
4401
+ const result = planModeBlockedMessage(toolCall.name);
4402
+ emitToolDone({
4403
+ type: "tool_done",
4404
+ id: toolCall.id,
4405
+ tool: toolCall.name,
4406
+ input: toolCall.input,
4407
+ result,
4408
+ isError: true,
4409
+ completedSideEffect: false,
4410
+ });
4411
+ recordToolResult(result, true);
4412
+ return {
4413
+ type: "tool-result",
4414
+ toolCallId: toolCall.id,
4415
+ toolName: toolCall.name,
4416
+ toolInput: wireToolInput,
4417
+ content: result,
4418
+ isError: true,
4419
+ };
4420
+ }
4351
4421
  let result;
4352
- if (visible) {
4353
- const repeats = (duplicateReadOnlyToolCalls.get(cacheKey) ?? 0) + 1;
4354
- duplicateReadOnlyToolCalls.set(cacheKey, repeats);
4355
- result = visibleDuplicateReadOnlyToolResult(toolCall.name);
4356
- if (repeats >= 3) {
4422
+ let isError = false;
4423
+ let mcpApp;
4424
+ let toolResultImages;
4425
+ try {
4426
+ // The run may have been aborted while we waited above for an
4427
+ // interrupted tool's ledger result (the wait can poll for minutes).
4428
+ // Re-check before invoking the action: starting it now would spawn a
4429
+ // fresh zombie execution — a duplicate side effect / double charge —
4430
+ // which the ledger-recovery path exists to prevent. The Promise.race
4431
+ // "Run aborted" leg below only rejects AFTER the action is invoked, so
4432
+ // it cannot guard this. Throw here instead, handled like any abort.
4433
+ if (signal.aborted) {
4434
+ throw new Error("Run aborted");
4435
+ }
4436
+ const timeoutSignal = AbortSignal.timeout(toolTimeoutMs);
4437
+ const actionUserEmail = opts.ownerEmail ?? getRequestUserEmail();
4438
+ const actionOrgId = opts.orgId ?? getRequestOrgId() ?? null;
4439
+ const actionContext = {
4440
+ send,
4441
+ userEmail: actionUserEmail ?? undefined,
4442
+ orgId: actionOrgId,
4443
+ appId: opts.appId,
4444
+ caller: opts.actionCaller ?? "tool",
4445
+ automation: opts.automation,
4446
+ networkProtocol: opts.networkProtocol,
4447
+ networkId: opts.networkId,
4448
+ networkPeer: opts.networkPeer,
4449
+ delegationDepth: opts.delegationDepth,
4450
+ visitedApps: opts.visitedApps,
4451
+ attachments: opts.attachments,
4452
+ signal,
4453
+ // Audit attribution: the action name + the agent thread/turn that
4454
+ // triggered this call, so a mutation can be traced to its run.
4455
+ actionName: toolCall.name,
4456
+ ...(wasApproved ? { approvedToolCallKey: approvalKey } : {}),
4457
+ ...(opts.threadId ? { threadId: opts.threadId } : {}),
4458
+ ...(opts.runId ? { runId: opts.runId } : {}),
4459
+ ...(opts.turnId ? { turnId: opts.turnId } : {}),
4460
+ };
4461
+ const requestContext = getRequestContext();
4462
+ const invokeAction = () => actionEntry.run(toolCall.input, actionContext);
4463
+ // Keep a reference to the action promise so we can attach a zombie-
4464
+ // detection continuation AFTER Promise.race abandons it on run abort.
4465
+ // The promise itself is not awaited here — Promise.race owns the await.
4466
+ const actionPromise = Promise.resolve(runWithRequestContext({
4467
+ ...(requestContext ?? {}),
4468
+ ...(actionUserEmail ? { userEmail: actionUserEmail } : {}),
4469
+ ...(actionOrgId ? { orgId: actionOrgId } : {}),
4470
+ ...(requestContext?.run ? { run: requestContext.run } : {}),
4471
+ }, invokeAction));
4472
+ // When the run is aborted (soft-timeout / user cancel) while this tool
4473
+ // call is in flight, Promise.race below will throw "Run aborted" and the
4474
+ // action's promise becomes a zombie — it keeps running but its result is
4475
+ // never returned to the loop. If the zombie eventually resolves, write
4476
+ // the result to the durable ledger keyed by (threadId, toolKey) so the
4477
+ // next continuation chunk can recover it instead of re-executing the
4478
+ // side effect.
4479
+ if (opts.threadId && !actionEntry.readOnly) {
4480
+ const ledgerThreadId = opts.threadId;
4481
+ const ledgerToolKey = toolCallCacheKey(toolCall.name, toolCall.input);
4482
+ actionPromise
4483
+ .then((zombieRaw) => {
4484
+ const zombieMcp = isMcpActionResult(zombieRaw)
4485
+ ? zombieRaw
4486
+ : null;
4487
+ if (zombieMcp &&
4488
+ zombieMcp.raw &&
4489
+ typeof zombieMcp.raw === "object" &&
4490
+ zombieMcp.raw.isError === true) {
4491
+ return;
4492
+ }
4493
+ const zombieText = zombieMcp ? zombieMcp.text : zombieRaw;
4494
+ const zombieStr = typeof zombieText === "string"
4495
+ ? zombieText
4496
+ : JSON.stringify(zombieText, null, 2);
4497
+ void writeLedgerEntry(ledgerThreadId, ledgerToolKey, zombieStr);
4498
+ })
4499
+ .catch(() => {
4500
+ // Action errored in the zombie — no result to ledger.
4501
+ });
4502
+ }
4503
+ const raw = await Promise.race([
4504
+ actionPromise,
4505
+ new Promise((_, reject) => {
4506
+ timeoutSignal.addEventListener("abort", () => reject(new Error(`Tool call timed out after ${toolTimeoutMs / 1000} seconds`)));
4507
+ }),
4508
+ // Stop waiting on the tool when the run itself is aborted (e.g. the
4509
+ // run-manager soft timeout, or a user cancel). Without this leg the
4510
+ // loop blocks on an in-flight tool for up to TOOL_TIMEOUT_MS after
4511
+ // the run signal has already fired.
4512
+ new Promise((_, reject) => {
4513
+ if (signal.aborted) {
4514
+ reject(new Error("Run aborted"));
4515
+ return;
4516
+ }
4517
+ signal.addEventListener("abort", () => reject(new Error("Run aborted")), { once: true });
4518
+ }),
4519
+ ]);
4520
+ const mcpResult = isMcpActionResult(raw) ? raw : null;
4521
+ const rawForAgent = mcpResult ? mcpResult.text : raw;
4522
+ if (mcpResult &&
4523
+ mcpResult.raw &&
4524
+ typeof mcpResult.raw === "object" &&
4525
+ mcpResult.raw.isError === true) {
4526
+ isError = true;
4527
+ }
4528
+ mcpApp = mcpResult?.mcpApp;
4529
+ // Demo mode is browser-local presentation state. The agent and MCP
4530
+ // layers always receive the real, access-scoped tool result.
4531
+ let resultForAgent = rawForAgent;
4532
+ // Vision images for the model: MCP tools return standard `image`
4533
+ // content parts; first-party actions opt in via the well-known
4534
+ // `_agentImages` result field (stripped from the JSON the model
4535
+ // reads). The images array
4536
+ // never touches the ledger — only the compact text notes appended
4537
+ // below are persisted.
4538
+ let imageNotes = [];
4539
+ if (mcpResult) {
4540
+ const mcpImages = extractMcpToolResultImages(mcpResult.raw);
4541
+ if (mcpImages.length > 0)
4542
+ toolResultImages = mcpImages;
4543
+ }
4544
+ else {
4545
+ const extracted = extractAgentImagesFromActionResult(resultForAgent);
4546
+ resultForAgent = extracted.value;
4547
+ imageNotes = extracted.notes;
4548
+ if (extracted.images.length > 0) {
4549
+ toolResultImages = extracted.images;
4550
+ }
4551
+ }
4552
+ if (toolResultImages) {
4553
+ imageNotes = [
4554
+ ...describeToolResultImages(toolResultImages),
4555
+ ...imageNotes,
4556
+ ];
4557
+ }
4558
+ let resultStr = typeof resultForAgent === "string"
4559
+ ? resultForAgent
4560
+ : JSON.stringify(resultForAgent, null, 2);
4561
+ if (resultStr.length > toolMaxResultChars) {
4562
+ const truncated = resultStr.slice(0, toolMaxResultChars);
4563
+ resultStr = `${truncated}\n\n...[truncated — full result was ${resultStr.length.toLocaleString()} chars; only first ${toolMaxResultChars.toLocaleString()} shown]`;
4564
+ }
4565
+ // Image notes go after truncation so they always survive into the
4566
+ // persisted result string (the durable record that an image existed).
4567
+ if (imageNotes.length > 0) {
4568
+ resultStr = `${resultStr}\n\n${imageNotes.join("\n")}`;
4569
+ }
4570
+ result = resultStr;
4571
+ if (toolCall.name === TOOL_SEARCH_ACTION_NAME && !isError) {
4572
+ const added = expandActiveTools(extractToolSearchResultNames(rawForAgent));
4573
+ if (added.length > 0) {
4574
+ result += `\n\nLoaded matching tool schemas for next step: ${added.join(", ")}`;
4575
+ }
4576
+ }
4577
+ }
4578
+ catch (err) {
4579
+ if (isAgentActionStopError(err)) {
4580
+ const message = sanitizeToolErrorValue(err.message) ||
4581
+ `Stopped after ${toolCall.name} failed.`;
4582
+ result = sanitizeToolErrorValue(err.toolResult || message);
4357
4583
  requestedActionStop ??= {
4358
- message: "I stopped because the agent kept asking for the same read-only context it already had. Please send the request again if you want me to retry from a fresh turn.",
4359
- errorCode: "duplicate_read_only_tool",
4584
+ message,
4585
+ ...(err.errorCode ? { errorCode: err.errorCode } : {}),
4360
4586
  };
4361
4587
  }
4588
+ else {
4589
+ const message = sanitizeToolErrorValue(err);
4590
+ result = `Error running ${toolCall.name}: ${message}${rateLimitRecoveryHint(message)}`;
4591
+ }
4592
+ isError = true;
4362
4593
  }
4363
- else {
4364
- // The earlier result was trimmed out of the model's visible
4365
- // context — this isn't a repetitive loop, the model legitimately
4366
- // can't see the answer anymore. Re-serve it in full and don't
4367
- // count a strike.
4368
- duplicateReadOnlyToolCalls.set(cacheKey, 0);
4369
- result = resurfacedDuplicateReadOnlyToolResult(toolCall.name, previousResult);
4594
+ if (isError) {
4595
+ result = finalizeToolErrorResult(result);
4370
4596
  }
4371
- send({
4372
- type: "tool_done",
4373
- id: toolCall.id,
4374
- tool: toolCall.name,
4375
- input: toolCall.input,
4376
- result,
4377
- completedSideEffect: false,
4378
- });
4379
- recordToolResult(result, false);
4380
- return {
4381
- type: "tool-result",
4382
- toolCallId: toolCall.id,
4383
- toolName: toolCall.name,
4384
- toolInput: wireToolInput,
4385
- content: result,
4386
- };
4387
- }
4388
- if (opts.executionMode === "plan" &&
4389
- !isPlanModeToolCallAllowed(toolCall.name, toolCall.input, actionEntry)) {
4390
- const result = planModeBlockedMessage(toolCall.name);
4391
- send({
4597
+ // Side-channel warnings raised anywhere inside the action's call stack
4598
+ // (see `action-warnings.ts`). Drained here rather than in the success
4599
+ // branch so an action that warns and then throws still surfaces it, and
4600
+ // so nothing stays pending to bleed into a later tool's result.
4601
+ const agentWarnings = drainAgentWarnings();
4602
+ if (agentWarnings.length > 0) {
4603
+ result = `${result}\n\n${formatAgentWarningsForToolResult(agentWarnings)}`;
4604
+ }
4605
+ // Auto-refresh the UI after a successful mutating tool call. Any call
4606
+ // that isn't read-only — by its own per-call Plan-mode effect, else the
4607
+ // action's readOnly flag — is assumed to mutate. The client's useDbSync
4608
+ // listener sees a change event with source:"action" and invalidates
4609
+ // ["action"] queries so list-* / get-* refetch. This makes refresh after
4610
+ // agent writes reliable without the model needing to remember to call
4611
+ // `refresh-screen` itself.
4612
+ if (!isError) {
4613
+ try {
4614
+ const { actionCallIsReadOnly, notifyActionChangeInBackground } = await import("../server/action-change.js");
4615
+ if (!actionCallIsReadOnly(actionEntry, toolCall.input, false)) {
4616
+ const owner = opts.ownerEmail ?? getRequestUserEmail() ?? undefined;
4617
+ const orgId = opts.orgId ?? getRequestOrgId() ?? undefined;
4618
+ notifyActionChangeInBackground({
4619
+ actionName: toolCall.name,
4620
+ ...(owner ? { owner } : {}),
4621
+ ...(orgId ? { orgId } : {}),
4622
+ });
4623
+ }
4624
+ }
4625
+ catch (error) {
4626
+ // poll module may be unavailable in non-server contexts — keep the
4627
+ // tool result, but make the failed background refresh observable.
4628
+ console.warn("Could not notify the action-change poller after a tool call", error);
4629
+ }
4630
+ }
4631
+ emitToolDone({
4392
4632
  type: "tool_done",
4393
4633
  id: toolCall.id,
4394
4634
  tool: toolCall.name,
4395
4635
  input: toolCall.input,
4396
4636
  result,
4397
- isError: true,
4398
- completedSideEffect: false,
4637
+ ...(isError ? { isError: true } : {}),
4638
+ ...(isError
4639
+ ? { completedSideEffect: false }
4640
+ : actionEntry.readOnly !== true
4641
+ ? { completedSideEffect: true }
4642
+ : {}),
4643
+ ...(mcpApp ? { mcpApp } : {}),
4644
+ ...(actionEntry.chatUI ? { chatUI: actionEntry.chatUI } : {}),
4399
4645
  });
4400
- recordToolResult(result, true);
4646
+ recordToolResult(result, isError);
4647
+ if (!isError) {
4648
+ if (cacheKey) {
4649
+ readOnlyToolResultCache.set(cacheKey, result);
4650
+ }
4651
+ else if (actionEntry.readOnly !== true) {
4652
+ // A genuine write invalidates all cached reads. A dedupe:false
4653
+ // read-only tool also has a null cacheKey (see above) but must NOT
4654
+ // clear the cache — it isn't a write and other tools' cached reads
4655
+ // are still valid.
4656
+ readOnlyToolResultCache.clear();
4657
+ duplicateReadOnlyToolCalls.clear();
4658
+ }
4659
+ }
4401
4660
  return {
4402
4661
  type: "tool-result",
4403
4662
  toolCallId: toolCall.id,
4404
4663
  toolName: toolCall.name,
4405
4664
  toolInput: wireToolInput,
4406
4665
  content: result,
4407
- isError: true,
4666
+ ...(isError ? { isError } : {}),
4667
+ ...(!isError && toolResultImages && toolResultImages.length > 0
4668
+ ? { images: toolResultImages }
4669
+ : {}),
4408
4670
  };
4409
4671
  }
4410
- let result;
4411
- let isError = false;
4412
- let mcpApp;
4413
- let toolResultImages;
4414
- try {
4415
- // The run may have been aborted while we waited above for an
4416
- // interrupted tool's ledger result (the wait can poll for minutes).
4417
- // Re-check before invoking the action: starting it now would spawn a
4418
- // fresh zombie execution — a duplicate side effect / double charge —
4419
- // which the ledger-recovery path exists to prevent. The Promise.race
4420
- // "Run aborted" leg below only rejects AFTER the action is invoked, so
4421
- // it cannot guard this. Throw here instead, handled like any abort.
4422
- if (signal.aborted) {
4423
- throw new Error("Run aborted");
4424
- }
4425
- const timeoutSignal = AbortSignal.timeout(toolTimeoutMs);
4426
- const actionUserEmail = opts.ownerEmail ?? getRequestUserEmail();
4427
- const actionOrgId = opts.orgId ?? getRequestOrgId() ?? null;
4428
- const actionContext = {
4429
- send,
4430
- userEmail: actionUserEmail ?? undefined,
4431
- orgId: actionOrgId,
4432
- appId: opts.appId,
4433
- caller: opts.actionCaller ?? "tool",
4434
- automation: opts.automation,
4435
- networkProtocol: opts.networkProtocol,
4436
- networkId: opts.networkId,
4437
- networkPeer: opts.networkPeer,
4438
- delegationDepth: opts.delegationDepth,
4439
- visitedApps: opts.visitedApps,
4440
- attachments: opts.attachments,
4441
- signal,
4442
- // Audit attribution: the action name + the agent thread/turn that
4443
- // triggered this call, so a mutation can be traced to its run.
4444
- actionName: toolCall.name,
4445
- ...(wasApproved ? { approvedToolCallKey: approvalKey } : {}),
4446
- ...(opts.threadId ? { threadId: opts.threadId } : {}),
4447
- ...(opts.runId ? { runId: opts.runId } : {}),
4448
- ...(opts.turnId ? { turnId: opts.turnId } : {}),
4449
- };
4450
- const requestContext = getRequestContext();
4451
- const invokeAction = () => actionEntry.run(toolCall.input, actionContext);
4452
- // Keep a reference to the action promise so we can attach a zombie-
4453
- // detection continuation AFTER Promise.race abandons it on run abort.
4454
- // The promise itself is not awaited here — Promise.race owns the await.
4455
- const actionPromise = Promise.resolve(runWithRequestContext({
4456
- ...(requestContext ?? {}),
4457
- ...(actionUserEmail ? { userEmail: actionUserEmail } : {}),
4458
- ...(actionOrgId ? { orgId: actionOrgId } : {}),
4459
- ...(requestContext?.run ? { run: requestContext.run } : {}),
4460
- }, invokeAction));
4461
- // When the run is aborted (soft-timeout / user cancel) while this tool
4462
- // call is in flight, Promise.race below will throw "Run aborted" and the
4463
- // action's promise becomes a zombie — it keeps running but its result is
4464
- // never returned to the loop. If the zombie eventually resolves, write
4465
- // the result to the durable ledger keyed by (threadId, toolKey) so the
4466
- // next continuation chunk can recover it instead of re-executing the
4467
- // side effect.
4468
- if (opts.threadId && !actionEntry.readOnly) {
4469
- const ledgerThreadId = opts.threadId;
4470
- const ledgerToolKey = toolCallCacheKey(toolCall.name, toolCall.input);
4471
- actionPromise
4472
- .then((zombieRaw) => {
4473
- const zombieMcp = isMcpActionResult(zombieRaw) ? zombieRaw : null;
4474
- if (zombieMcp &&
4475
- zombieMcp.raw &&
4476
- typeof zombieMcp.raw === "object" &&
4477
- zombieMcp.raw.isError === true) {
4478
- return;
4479
- }
4480
- const zombieText = zombieMcp ? zombieMcp.text : zombieRaw;
4481
- const zombieStr = typeof zombieText === "string"
4482
- ? zombieText
4483
- : JSON.stringify(zombieText, null, 2);
4484
- void writeLedgerEntry(ledgerThreadId, ledgerToolKey, zombieStr);
4485
- })
4486
- .catch(() => {
4487
- // Action errored in the zombie — no result to ledger.
4672
+ finally {
4673
+ if (!toolDoneEmitted) {
4674
+ const result = `Error running ${toolCall.name}: tool execution ended before returning a result.`;
4675
+ emitToolDone({
4676
+ type: "tool_done",
4677
+ id: toolCall.id,
4678
+ tool: toolCall.name,
4679
+ input: toolCall.input,
4680
+ result,
4681
+ isError: true,
4682
+ completedSideEffect: false,
4488
4683
  });
4489
- }
4490
- const raw = await Promise.race([
4491
- actionPromise,
4492
- new Promise((_, reject) => {
4493
- timeoutSignal.addEventListener("abort", () => reject(new Error(`Tool call timed out after ${toolTimeoutMs / 1000} seconds`)));
4494
- }),
4495
- // Stop waiting on the tool when the run itself is aborted (e.g. the
4496
- // run-manager soft timeout, or a user cancel). Without this leg the
4497
- // loop blocks on an in-flight tool for up to TOOL_TIMEOUT_MS after
4498
- // the run signal has already fired.
4499
- new Promise((_, reject) => {
4500
- if (signal.aborted) {
4501
- reject(new Error("Run aborted"));
4502
- return;
4503
- }
4504
- signal.addEventListener("abort", () => reject(new Error("Run aborted")), { once: true });
4505
- }),
4506
- ]);
4507
- const mcpResult = isMcpActionResult(raw) ? raw : null;
4508
- const rawForAgent = mcpResult ? mcpResult.text : raw;
4509
- if (mcpResult &&
4510
- mcpResult.raw &&
4511
- typeof mcpResult.raw === "object" &&
4512
- mcpResult.raw.isError === true) {
4513
- isError = true;
4514
- }
4515
- mcpApp = mcpResult?.mcpApp;
4516
- // Demo mode is browser-local presentation state. The agent and MCP
4517
- // layers always receive the real, access-scoped tool result.
4518
- let resultForAgent = rawForAgent;
4519
- // Vision images for the model: MCP tools return standard `image`
4520
- // content parts; first-party actions opt in via the well-known
4521
- // `_agentImages` result field (stripped from the JSON the model
4522
- // reads). The images array
4523
- // never touches the ledger — only the compact text notes appended
4524
- // below are persisted.
4525
- let imageNotes = [];
4526
- if (mcpResult) {
4527
- const mcpImages = extractMcpToolResultImages(mcpResult.raw);
4528
- if (mcpImages.length > 0)
4529
- toolResultImages = mcpImages;
4530
- }
4531
- else {
4532
- const extracted = extractAgentImagesFromActionResult(resultForAgent);
4533
- resultForAgent = extracted.value;
4534
- imageNotes = extracted.notes;
4535
- if (extracted.images.length > 0) {
4536
- toolResultImages = extracted.images;
4537
- }
4538
- }
4539
- if (toolResultImages) {
4540
- imageNotes = [
4541
- ...describeToolResultImages(toolResultImages),
4542
- ...imageNotes,
4543
- ];
4544
- }
4545
- let resultStr = typeof resultForAgent === "string"
4546
- ? resultForAgent
4547
- : JSON.stringify(resultForAgent, null, 2);
4548
- if (resultStr.length > toolMaxResultChars) {
4549
- const truncated = resultStr.slice(0, toolMaxResultChars);
4550
- resultStr = `${truncated}\n\n...[truncated — full result was ${resultStr.length.toLocaleString()} chars; only first ${toolMaxResultChars.toLocaleString()} shown]`;
4551
- }
4552
- // Image notes go after truncation so they always survive into the
4553
- // persisted result string (the durable record that an image existed).
4554
- if (imageNotes.length > 0) {
4555
- resultStr = `${resultStr}\n\n${imageNotes.join("\n")}`;
4556
- }
4557
- result = resultStr;
4558
- if (toolCall.name === TOOL_SEARCH_ACTION_NAME && !isError) {
4559
- const added = expandActiveTools(extractToolSearchResultNames(rawForAgent));
4560
- if (added.length > 0) {
4561
- result += `\n\nLoaded matching tool schemas for next step: ${added.join(", ")}`;
4562
- }
4563
- }
4564
- }
4565
- catch (err) {
4566
- if (isAgentActionStopError(err)) {
4567
- const message = sanitizeToolErrorValue(err.message) ||
4568
- `Stopped after ${toolCall.name} failed.`;
4569
- result = sanitizeToolErrorValue(err.toolResult || message);
4570
- requestedActionStop ??= {
4571
- message,
4572
- ...(err.errorCode ? { errorCode: err.errorCode } : {}),
4573
- };
4574
- }
4575
- else {
4576
- const message = sanitizeToolErrorValue(err);
4577
- result = `Error running ${toolCall.name}: ${message}${rateLimitRecoveryHint(message)}`;
4578
- }
4579
- isError = true;
4580
- }
4581
- if (isError) {
4582
- result = finalizeToolErrorResult(result);
4583
- }
4584
- // Side-channel warnings raised anywhere inside the action's call stack
4585
- // (see `action-warnings.ts`). Drained here rather than in the success
4586
- // branch so an action that warns and then throws still surfaces it, and
4587
- // so nothing stays pending to bleed into a later tool's result.
4588
- const agentWarnings = drainAgentWarnings();
4589
- if (agentWarnings.length > 0) {
4590
- result = `${result}\n\n${formatAgentWarningsForToolResult(agentWarnings)}`;
4591
- }
4592
- // Auto-refresh the UI after a successful mutating tool call. Any call
4593
- // that isn't read-only — by its own per-call Plan-mode effect, else the
4594
- // action's readOnly flag — is assumed to mutate. The client's useDbSync
4595
- // listener sees a change event with source:"action" and invalidates
4596
- // ["action"] queries so list-* / get-* refetch. This makes refresh after
4597
- // agent writes reliable without the model needing to remember to call
4598
- // `refresh-screen` itself.
4599
- if (!isError) {
4600
- try {
4601
- const { actionCallIsReadOnly, notifyActionChangeInBackground } = await import("../server/action-change.js");
4602
- if (!actionCallIsReadOnly(actionEntry, toolCall.input, false)) {
4603
- const owner = opts.ownerEmail ?? getRequestUserEmail() ?? undefined;
4604
- const orgId = opts.orgId ?? getRequestOrgId() ?? undefined;
4605
- notifyActionChangeInBackground({
4606
- actionName: toolCall.name,
4607
- ...(owner ? { owner } : {}),
4608
- ...(orgId ? { orgId } : {}),
4609
- });
4610
- }
4611
- }
4612
- catch {
4613
- // poll module may be unavailable in non-server contexts — ignore
4614
- }
4615
- }
4616
- send({
4617
- type: "tool_done",
4618
- id: toolCall.id,
4619
- tool: toolCall.name,
4620
- input: toolCall.input,
4621
- result,
4622
- ...(isError ? { isError: true } : {}),
4623
- ...(isError
4624
- ? { completedSideEffect: false }
4625
- : actionEntry.readOnly !== true
4626
- ? { completedSideEffect: true }
4627
- : {}),
4628
- ...(mcpApp ? { mcpApp } : {}),
4629
- ...(actionEntry.chatUI ? { chatUI: actionEntry.chatUI } : {}),
4630
- });
4631
- recordToolResult(result, isError);
4632
- if (!isError) {
4633
- if (cacheKey) {
4634
- readOnlyToolResultCache.set(cacheKey, result);
4635
- }
4636
- else if (actionEntry.readOnly !== true) {
4637
- // A genuine write invalidates all cached reads. A dedupe:false
4638
- // read-only tool also has a null cacheKey (see above) but must NOT
4639
- // clear the cache — it isn't a write and other tools' cached reads
4640
- // are still valid.
4641
- readOnlyToolResultCache.clear();
4642
- duplicateReadOnlyToolCalls.clear();
4684
+ recordToolResult(result, true);
4643
4685
  }
4644
4686
  }
4645
- return {
4646
- type: "tool-result",
4647
- toolCallId: toolCall.id,
4648
- toolName: toolCall.name,
4649
- toolInput: wireToolInput,
4650
- content: result,
4651
- ...(isError ? { isError } : {}),
4652
- ...(!isError && toolResultImages && toolResultImages.length > 0
4653
- ? { images: toolResultImages }
4654
- : {}),
4655
- };
4656
4687
  };
4657
4688
  const getParallelBatchKind = (toolCall) => {
4658
4689
  const entry = actions[toolCall.name];