@agent-native/core 0.7.83 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/action.js +1 -1
  2. package/dist/action.js.map +1 -1
  3. package/dist/agent/production-agent.d.ts.map +1 -1
  4. package/dist/agent/production-agent.js +8 -8
  5. package/dist/agent/production-agent.js.map +1 -1
  6. package/dist/agent/run-manager.d.ts +2 -0
  7. package/dist/agent/run-manager.d.ts.map +1 -1
  8. package/dist/agent/run-manager.js +44 -18
  9. package/dist/agent/run-manager.js.map +1 -1
  10. package/dist/agent/types.d.ts +1 -1
  11. package/dist/agent/types.d.ts.map +1 -1
  12. package/dist/agent/types.js.map +1 -1
  13. package/dist/cli/create.d.ts.map +1 -1
  14. package/dist/cli/create.js +54 -11
  15. package/dist/cli/create.js.map +1 -1
  16. package/dist/cli/workspacify.d.ts.map +1 -1
  17. package/dist/cli/workspacify.js +12 -9
  18. package/dist/cli/workspacify.js.map +1 -1
  19. package/dist/client/AgentPanel.d.ts +1 -1
  20. package/dist/client/AgentPanel.d.ts.map +1 -1
  21. package/dist/client/AgentPanel.js +22 -1
  22. package/dist/client/AgentPanel.js.map +1 -1
  23. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  24. package/dist/client/agent-chat-adapter.js +254 -29
  25. package/dist/client/agent-chat-adapter.js.map +1 -1
  26. package/dist/client/agent-chat.d.ts +2 -0
  27. package/dist/client/agent-chat.d.ts.map +1 -1
  28. package/dist/client/agent-chat.js +11 -2
  29. package/dist/client/agent-chat.js.map +1 -1
  30. package/dist/client/composer/ComposerPlusMenu.js +1 -1
  31. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  32. package/dist/client/composer/PromptComposer.js +1 -1
  33. package/dist/client/composer/PromptComposer.js.map +1 -1
  34. package/dist/client/composer/TiptapComposer.js +8 -8
  35. package/dist/client/composer/TiptapComposer.js.map +1 -1
  36. package/dist/client/composer/types.d.ts +1 -1
  37. package/dist/client/composer/types.d.ts.map +1 -1
  38. package/dist/client/composer/types.js.map +1 -1
  39. package/dist/client/extensions/EmbeddedExtension.d.ts +20 -0
  40. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -0
  41. package/dist/client/{tools/EmbeddedTool.js → extensions/EmbeddedExtension.js} +41 -41
  42. package/dist/client/extensions/EmbeddedExtension.js.map +1 -0
  43. package/dist/client/extensions/ExtensionEditor.d.ts +5 -0
  44. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -0
  45. package/dist/client/extensions/ExtensionEditor.js +129 -0
  46. package/dist/client/extensions/ExtensionEditor.js.map +1 -0
  47. package/dist/client/{tools → extensions}/ExtensionSlot.d.ts +3 -3
  48. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -0
  49. package/dist/client/{tools → extensions}/ExtensionSlot.js +14 -14
  50. package/dist/client/extensions/ExtensionSlot.js.map +1 -0
  51. package/dist/client/extensions/ExtensionViewer.d.ts +5 -0
  52. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -0
  53. package/dist/client/{tools/ToolViewer.js → extensions/ExtensionViewer.js} +67 -65
  54. package/dist/client/extensions/ExtensionViewer.js.map +1 -0
  55. package/dist/client/extensions/ExtensionViewerPage.d.ts +2 -0
  56. package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -0
  57. package/dist/client/{tools/ToolViewerPage.js → extensions/ExtensionViewerPage.js} +8 -8
  58. package/dist/client/extensions/ExtensionViewerPage.js.map +1 -0
  59. package/dist/client/extensions/ExtensionsListPage.d.ts +2 -0
  60. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -0
  61. package/dist/client/extensions/ExtensionsListPage.js +67 -0
  62. package/dist/client/extensions/ExtensionsListPage.js.map +1 -0
  63. package/dist/client/extensions/ExtensionsSidebarSection.d.ts +2 -0
  64. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -0
  65. package/dist/client/{tools/ToolsSidebarSection.js → extensions/ExtensionsSidebarSection.js} +58 -58
  66. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -0
  67. package/dist/client/{tools/tool-order.d.ts → extensions/extension-order.d.ts} +2 -2
  68. package/dist/client/extensions/extension-order.d.ts.map +1 -0
  69. package/dist/client/{tools/tool-order.js → extensions/extension-order.js} +3 -3
  70. package/dist/client/extensions/extension-order.js.map +1 -0
  71. package/dist/client/{tools → extensions}/iframe-bridge.d.ts +11 -11
  72. package/dist/client/extensions/iframe-bridge.d.ts.map +1 -0
  73. package/dist/client/{tools → extensions}/iframe-bridge.js +24 -24
  74. package/dist/client/extensions/iframe-bridge.js.map +1 -0
  75. package/dist/client/extensions/index.d.ts +14 -0
  76. package/dist/client/extensions/index.d.ts.map +1 -0
  77. package/dist/client/extensions/index.js +19 -0
  78. package/dist/client/extensions/index.js.map +1 -0
  79. package/dist/client/sse-event-processor.d.ts +2 -1
  80. package/dist/client/sse-event-processor.d.ts.map +1 -1
  81. package/dist/client/sse-event-processor.js +87 -6
  82. package/dist/client/sse-event-processor.js.map +1 -1
  83. package/dist/extensions/actions.d.ts +3 -0
  84. package/dist/extensions/actions.d.ts.map +1 -0
  85. package/dist/{tools → extensions}/actions.js +54 -51
  86. package/dist/extensions/actions.js.map +1 -0
  87. package/dist/{tools → extensions}/fetch-tool.d.ts +4 -0
  88. package/dist/extensions/fetch-tool.d.ts.map +1 -0
  89. package/dist/{tools → extensions}/fetch-tool.js +12 -7
  90. package/dist/extensions/fetch-tool.js.map +1 -0
  91. package/dist/extensions/html-shell.d.ts +56 -0
  92. package/dist/extensions/html-shell.d.ts.map +1 -0
  93. package/dist/{tools → extensions}/html-shell.js +101 -83
  94. package/dist/extensions/html-shell.js.map +1 -0
  95. package/dist/{tools → extensions}/proxy-security.d.ts +2 -2
  96. package/dist/extensions/proxy-security.d.ts.map +1 -0
  97. package/dist/{tools → extensions}/proxy-security.js +3 -3
  98. package/dist/extensions/proxy-security.js.map +1 -0
  99. package/dist/extensions/routes.d.ts +2 -0
  100. package/dist/extensions/routes.d.ts.map +1 -0
  101. package/dist/{tools → extensions}/routes.js +73 -69
  102. package/dist/extensions/routes.js.map +1 -0
  103. package/dist/{tools → extensions}/schema.d.ts +44 -38
  104. package/dist/extensions/schema.d.ts.map +1 -0
  105. package/dist/{tools → extensions}/schema.js +41 -34
  106. package/dist/extensions/schema.js.map +1 -0
  107. package/dist/extensions/slots/routes.d.ts +15 -0
  108. package/dist/extensions/slots/routes.d.ts.map +1 -0
  109. package/dist/{tools → extensions}/slots/routes.js +26 -26
  110. package/dist/extensions/slots/routes.js.map +1 -0
  111. package/dist/{tools → extensions}/slots/schema.d.ts +24 -21
  112. package/dist/extensions/slots/schema.d.ts.map +1 -0
  113. package/dist/extensions/slots/schema.js +79 -0
  114. package/dist/extensions/slots/schema.js.map +1 -0
  115. package/dist/extensions/slots/store.d.ts +66 -0
  116. package/dist/extensions/slots/store.d.ts.map +1 -0
  117. package/dist/extensions/slots/store.js +238 -0
  118. package/dist/extensions/slots/store.js.map +1 -0
  119. package/dist/extensions/store.d.ts +40 -0
  120. package/dist/extensions/store.d.ts.map +1 -0
  121. package/dist/{tools → extensions}/store.js +59 -54
  122. package/dist/extensions/store.js.map +1 -0
  123. package/dist/extensions/theme.d.ts.map +1 -0
  124. package/dist/extensions/theme.js.map +1 -0
  125. package/dist/{tools → extensions}/url-safety.d.ts +5 -3
  126. package/dist/extensions/url-safety.d.ts.map +1 -0
  127. package/dist/{tools → extensions}/url-safety.js +11 -4
  128. package/dist/extensions/url-safety.js.map +1 -0
  129. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  130. package/dist/server/agent-chat-plugin.js +12 -10
  131. package/dist/server/agent-chat-plugin.js.map +1 -1
  132. package/dist/server/core-routes-plugin.d.ts +15 -0
  133. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  134. package/dist/server/core-routes-plugin.js +64 -10
  135. package/dist/server/core-routes-plugin.js.map +1 -1
  136. package/dist/server/csrf.d.ts +3 -2
  137. package/dist/server/csrf.d.ts.map +1 -1
  138. package/dist/server/csrf.js +3 -2
  139. package/dist/server/csrf.js.map +1 -1
  140. package/dist/shared/workspace-app-id.d.ts +1 -1
  141. package/dist/shared/workspace-app-id.d.ts.map +1 -1
  142. package/dist/shared/workspace-app-id.js +5 -1
  143. package/dist/shared/workspace-app-id.js.map +1 -1
  144. package/dist/templates/workspace-root/README.md +5 -4
  145. package/dist/usage/store.d.ts +1 -1
  146. package/dist/usage/store.d.ts.map +1 -1
  147. package/dist/usage/store.js +1 -1
  148. package/dist/usage/store.js.map +1 -1
  149. package/dist/vite/client.d.ts.map +1 -1
  150. package/dist/vite/client.js +10 -1
  151. package/dist/vite/client.js.map +1 -1
  152. package/docs/content/actions.md +10 -10
  153. package/docs/content/extensions.md +230 -0
  154. package/docs/content/key-concepts.md +2 -2
  155. package/docs/content/server.md +13 -13
  156. package/docs/content/sharing.md +2 -2
  157. package/docs/content/template-dispatch.md +5 -0
  158. package/docs/content/what-is-agent-native.md +1 -1
  159. package/package.json +22 -17
  160. package/src/templates/workspace-root/README.md +5 -4
  161. package/dist/client/tools/EmbeddedTool.d.ts +0 -20
  162. package/dist/client/tools/EmbeddedTool.d.ts.map +0 -1
  163. package/dist/client/tools/EmbeddedTool.js.map +0 -1
  164. package/dist/client/tools/ExtensionSlot.d.ts.map +0 -1
  165. package/dist/client/tools/ExtensionSlot.js.map +0 -1
  166. package/dist/client/tools/ToolEditor.d.ts +0 -5
  167. package/dist/client/tools/ToolEditor.d.ts.map +0 -1
  168. package/dist/client/tools/ToolEditor.js +0 -129
  169. package/dist/client/tools/ToolEditor.js.map +0 -1
  170. package/dist/client/tools/ToolViewer.d.ts +0 -5
  171. package/dist/client/tools/ToolViewer.d.ts.map +0 -1
  172. package/dist/client/tools/ToolViewer.js.map +0 -1
  173. package/dist/client/tools/ToolViewerPage.d.ts +0 -2
  174. package/dist/client/tools/ToolViewerPage.d.ts.map +0 -1
  175. package/dist/client/tools/ToolViewerPage.js.map +0 -1
  176. package/dist/client/tools/ToolsListPage.d.ts +0 -2
  177. package/dist/client/tools/ToolsListPage.d.ts.map +0 -1
  178. package/dist/client/tools/ToolsListPage.js +0 -67
  179. package/dist/client/tools/ToolsListPage.js.map +0 -1
  180. package/dist/client/tools/ToolsSidebarSection.d.ts +0 -2
  181. package/dist/client/tools/ToolsSidebarSection.d.ts.map +0 -1
  182. package/dist/client/tools/ToolsSidebarSection.js.map +0 -1
  183. package/dist/client/tools/iframe-bridge.d.ts.map +0 -1
  184. package/dist/client/tools/iframe-bridge.js.map +0 -1
  185. package/dist/client/tools/index.d.ts +0 -8
  186. package/dist/client/tools/index.d.ts.map +0 -1
  187. package/dist/client/tools/index.js +0 -8
  188. package/dist/client/tools/index.js.map +0 -1
  189. package/dist/client/tools/tool-order.d.ts.map +0 -1
  190. package/dist/client/tools/tool-order.js.map +0 -1
  191. package/dist/tools/actions.d.ts +0 -3
  192. package/dist/tools/actions.d.ts.map +0 -1
  193. package/dist/tools/actions.js.map +0 -1
  194. package/dist/tools/fetch-tool.d.ts.map +0 -1
  195. package/dist/tools/fetch-tool.js.map +0 -1
  196. package/dist/tools/html-shell.d.ts +0 -45
  197. package/dist/tools/html-shell.d.ts.map +0 -1
  198. package/dist/tools/html-shell.js.map +0 -1
  199. package/dist/tools/proxy-security.d.ts.map +0 -1
  200. package/dist/tools/proxy-security.js.map +0 -1
  201. package/dist/tools/routes.d.ts +0 -2
  202. package/dist/tools/routes.d.ts.map +0 -1
  203. package/dist/tools/routes.js.map +0 -1
  204. package/dist/tools/schema.d.ts.map +0 -1
  205. package/dist/tools/schema.js.map +0 -1
  206. package/dist/tools/slots/routes.d.ts +0 -15
  207. package/dist/tools/slots/routes.d.ts.map +0 -1
  208. package/dist/tools/slots/routes.js.map +0 -1
  209. package/dist/tools/slots/schema.d.ts.map +0 -1
  210. package/dist/tools/slots/schema.js +0 -76
  211. package/dist/tools/slots/schema.js.map +0 -1
  212. package/dist/tools/slots/store.d.ts +0 -66
  213. package/dist/tools/slots/store.d.ts.map +0 -1
  214. package/dist/tools/slots/store.js +0 -227
  215. package/dist/tools/slots/store.js.map +0 -1
  216. package/dist/tools/store.d.ts +0 -40
  217. package/dist/tools/store.d.ts.map +0 -1
  218. package/dist/tools/store.js.map +0 -1
  219. package/dist/tools/theme.d.ts.map +0 -1
  220. package/dist/tools/theme.js.map +0 -1
  221. package/dist/tools/url-safety.d.ts.map +0 -1
  222. package/dist/tools/url-safety.js.map +0 -1
  223. package/docs/content/tools.md +0 -205
  224. /package/dist/{tools → extensions}/theme.d.ts +0 -0
  225. /package/dist/{tools → extensions}/theme.js +0 -0
@@ -1,20 +1,20 @@
1
- import { createTool, getTool, updateTool, updateToolContent } from "./store.js";
2
- import { addToolSlotTarget, installToolSlot, uninstallToolSlot, listToolsForSlot, listSlotsForTool, } from "./slots/store.js";
3
- export function createToolActionEntries() {
1
+ import { createExtension, getExtension, updateExtension, updateExtensionContent, } from "./store.js";
2
+ import { addExtensionSlotTarget, installExtensionSlot, uninstallExtensionSlot, listExtensionsForSlot, listSlotsForExtension, } from "./slots/store.js";
3
+ export function createExtensionActionEntries() {
4
4
  return {
5
- "create-tool": {
5
+ "create-extension": {
6
6
  tool: {
7
- description: "Create a sandboxed Alpine.js mini-app tool. Use this when the user asks to create, build, or make a tool/widget/dashboard/calculator. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), dbExec(), toolFetch(), and toolData.",
7
+ description: "Create a sandboxed Alpine.js mini-app extension. Use this when the user asks to create, build, or make an extension/widget/dashboard/calculator. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), dbExec(), extensionFetch(), and extensionData.",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
11
11
  name: {
12
12
  type: "string",
13
- description: 'Short display name for the tool. Do not include "app" — e.g. name a todo app "Todos", a weather app "Weather".',
13
+ description: 'Short display name for the extension. Do not include "app" — e.g. name a todo app "Todos", a weather app "Weather".',
14
14
  },
15
15
  description: {
16
16
  type: "string",
17
- description: "One-sentence summary of what the tool does.",
17
+ description: "One-sentence summary of what the extension does.",
18
18
  },
19
19
  content: {
20
20
  type: "string",
@@ -35,7 +35,7 @@ export function createToolActionEntries() {
35
35
  return "Error: name is required.";
36
36
  if (!content)
37
37
  return "Error: content is required.";
38
- const tool = await createTool({
38
+ const extension = await createExtension({
39
39
  name,
40
40
  description: String(args?.description ?? "").trim(),
41
41
  content,
@@ -43,20 +43,20 @@ export function createToolActionEntries() {
43
43
  });
44
44
  return {
45
45
  ok: true,
46
- tool,
47
- next: `Navigate to /tools/${tool.id} or use the navigate action with --view=tools --toolId=${tool.id}.`,
46
+ extension,
47
+ next: `Navigate to /extensions/${extension.id} or use the navigate action with --view=extensions --extensionId=${extension.id}.`,
48
48
  };
49
49
  },
50
50
  },
51
- "update-tool": {
51
+ "update-extension": {
52
52
  tool: {
53
- description: "Update an existing sandboxed Alpine.js mini-app tool. Prefer patches for surgical edits; use full content replacement only when necessary.",
53
+ description: "Update an existing sandboxed Alpine.js mini-app extension. Prefer patches for surgical edits; use full content replacement only when necessary.",
54
54
  parameters: {
55
55
  type: "object",
56
56
  properties: {
57
57
  id: {
58
58
  type: "string",
59
- description: "Tool id to update.",
59
+ description: "Extension id to update.",
60
60
  },
61
61
  name: {
62
62
  type: "string",
@@ -97,7 +97,7 @@ export function createToolActionEntries() {
97
97
  if (args?.patches !== undefined && !patches) {
98
98
  return "Error: patches must be a JSON array of { find, replace } objects.";
99
99
  }
100
- result = await updateToolContent(id, {
100
+ result = await updateExtensionContent(id, {
101
101
  content: args?.content !== undefined ? String(args.content) : undefined,
102
102
  patches,
103
103
  });
@@ -114,22 +114,22 @@ export function createToolActionEntries() {
114
114
  meta.visibility = String(args.visibility);
115
115
  }
116
116
  if (Object.keys(meta).length > 0) {
117
- result = await updateTool(id, meta);
117
+ result = await updateExtension(id, meta);
118
118
  }
119
119
  if (!result)
120
- result = await getTool(id);
120
+ result = await getExtension(id);
121
121
  if (!result)
122
- return `Error: tool not found: ${id}`;
123
- return { ok: true, tool: result };
122
+ return `Error: extension not found: ${id}`;
123
+ return { ok: true, extension: result };
124
124
  },
125
125
  },
126
- "add-tool-slot-target": {
126
+ "add-extension-slot-target": {
127
127
  tool: {
128
- description: 'Declare that a tool can render in a UI extension-point slot of an app (e.g. "mail.contact-sidebar.bottom"). Apps drop ExtensionSlot components in their UI; this action registers a tool as installable into one of those slots. Slot IDs follow the convention <app>.<area>.<position>. Caller must have editor access to the tool.',
128
+ description: 'Declare that an extension can render in a UI extension-point slot of an app (e.g. "mail.contact-sidebar.bottom"). Apps drop ExtensionSlot components in their UI; this action registers an extension as installable into one of those slots. Slot IDs follow the convention <app>.<area>.<position>. Caller must have editor access to the extension.',
129
129
  parameters: {
130
130
  type: "object",
131
131
  properties: {
132
- toolId: { type: "string", description: "Tool id." },
132
+ extensionId: { type: "string", description: "Extension id." },
133
133
  slotId: {
134
134
  type: "string",
135
135
  description: 'Slot identifier — e.g. "mail.contact-sidebar.bottom".',
@@ -139,27 +139,30 @@ export function createToolActionEntries() {
139
139
  description: "Optional JSON string with slot-specific config (defaults, hints, etc.).",
140
140
  },
141
141
  },
142
- required: ["toolId", "slotId"],
142
+ required: ["extensionId", "slotId"],
143
143
  },
144
144
  },
145
145
  run: async (args) => {
146
- const toolId = String(args?.toolId ?? "").trim();
146
+ const extensionId = String(args?.extensionId ?? "").trim();
147
147
  const slotId = String(args?.slotId ?? "").trim();
148
- if (!toolId)
149
- return "Error: toolId is required.";
148
+ if (!extensionId)
149
+ return "Error: extensionId is required.";
150
150
  if (!slotId)
151
151
  return "Error: slotId is required.";
152
- const row = await addToolSlotTarget(toolId, slotId, args?.config ? String(args.config) : undefined);
152
+ const row = await addExtensionSlotTarget(extensionId, slotId, args?.config ? String(args.config) : undefined);
153
153
  return { ok: true, slot: row };
154
154
  },
155
155
  },
156
156
  "install-extension": {
157
157
  tool: {
158
- description: "Install a tool as a widget in an extension-point slot for the current user. The tool must already declare the slot via add-tool-slot-target. Per-user installation — only affects the calling user's view. Use after creating a tool that targets a slot, or when the user asks to add an existing widget to a slot.",
158
+ description: "Install an extension as a widget in an extension-point slot for the current user. The extension must already declare the slot via add-extension-slot-target. Per-user installation — only affects the calling user's view. Use after creating an extension that targets a slot, or when the user asks to add an existing widget to a slot.",
159
159
  parameters: {
160
160
  type: "object",
161
161
  properties: {
162
- toolId: { type: "string", description: "Tool id to install." },
162
+ extensionId: {
163
+ type: "string",
164
+ description: "Extension id to install.",
165
+ },
163
166
  slotId: {
164
167
  type: "string",
165
168
  description: 'Slot identifier — e.g. "mail.contact-sidebar.bottom".',
@@ -173,20 +176,20 @@ export function createToolActionEntries() {
173
176
  description: "Optional JSON string with per-install config (overrides, settings).",
174
177
  },
175
178
  },
176
- required: ["toolId", "slotId"],
179
+ required: ["extensionId", "slotId"],
177
180
  },
178
181
  },
179
182
  run: async (args) => {
180
- const toolId = String(args?.toolId ?? "").trim();
183
+ const extensionId = String(args?.extensionId ?? "").trim();
181
184
  const slotId = String(args?.slotId ?? "").trim();
182
- if (!toolId)
183
- return "Error: toolId is required.";
185
+ if (!extensionId)
186
+ return "Error: extensionId is required.";
184
187
  if (!slotId)
185
188
  return "Error: slotId is required.";
186
189
  const position = args?.position !== undefined && args.position !== null
187
190
  ? Number(args.position)
188
191
  : undefined;
189
- const row = await installToolSlot(toolId, slotId, {
192
+ const row = await installExtensionSlot(extensionId, slotId, {
190
193
  position: Number.isFinite(position) ? position : undefined,
191
194
  config: args?.config ? String(args.config) : undefined,
192
195
  });
@@ -195,30 +198,30 @@ export function createToolActionEntries() {
195
198
  },
196
199
  "uninstall-extension": {
197
200
  tool: {
198
- description: "Remove a tool from an extension-point slot for the current user. Does not delete the tool itself.",
201
+ description: "Remove an extension from an extension-point slot for the current user. Does not delete the extension itself.",
199
202
  parameters: {
200
203
  type: "object",
201
204
  properties: {
202
- toolId: { type: "string", description: "Tool id." },
205
+ extensionId: { type: "string", description: "Extension id." },
203
206
  slotId: { type: "string", description: "Slot identifier." },
204
207
  },
205
- required: ["toolId", "slotId"],
208
+ required: ["extensionId", "slotId"],
206
209
  },
207
210
  },
208
211
  run: async (args) => {
209
- const toolId = String(args?.toolId ?? "").trim();
212
+ const extensionId = String(args?.extensionId ?? "").trim();
210
213
  const slotId = String(args?.slotId ?? "").trim();
211
- if (!toolId)
212
- return "Error: toolId is required.";
214
+ if (!extensionId)
215
+ return "Error: extensionId is required.";
213
216
  if (!slotId)
214
217
  return "Error: slotId is required.";
215
- await uninstallToolSlot(toolId, slotId);
218
+ await uninstallExtensionSlot(extensionId, slotId);
216
219
  return { ok: true };
217
220
  },
218
221
  },
219
- "list-tools-for-slot": {
222
+ "list-extensions-for-slot": {
220
223
  tool: {
221
- description: "List tools the current user has access to that declare a given extension-point slot. Use to discover what's available to install into a slot the user mentioned.",
224
+ description: "List extensions the current user has access to that declare a given extension-point slot. Use to discover what's available to install into a slot the user mentioned.",
222
225
  parameters: {
223
226
  type: "object",
224
227
  properties: {
@@ -231,26 +234,26 @@ export function createToolActionEntries() {
231
234
  const slotId = String(args?.slotId ?? "").trim();
232
235
  if (!slotId)
233
236
  return "Error: slotId is required.";
234
- return { tools: await listToolsForSlot(slotId) };
237
+ return { extensions: await listExtensionsForSlot(slotId) };
235
238
  },
236
239
  readOnly: true,
237
240
  },
238
- "list-tool-slots": {
241
+ "list-extension-slots": {
239
242
  tool: {
240
- description: "List the extension-point slots a specific tool declares it can render in. Caller must have viewer access to the tool.",
243
+ description: "List the extension-point slots a specific extension declares it can render in. Caller must have viewer access to the extension.",
241
244
  parameters: {
242
245
  type: "object",
243
246
  properties: {
244
- toolId: { type: "string", description: "Tool id." },
247
+ extensionId: { type: "string", description: "Extension id." },
245
248
  },
246
- required: ["toolId"],
249
+ required: ["extensionId"],
247
250
  },
248
251
  },
249
252
  run: async (args) => {
250
- const toolId = String(args?.toolId ?? "").trim();
251
- if (!toolId)
252
- return "Error: toolId is required.";
253
- return { slots: await listSlotsForTool(toolId) };
253
+ const extensionId = String(args?.extensionId ?? "").trim();
254
+ if (!extensionId)
255
+ return "Error: extensionId is required.";
256
+ return { slots: await listSlotsForExtension(extensionId) };
254
257
  },
255
258
  readOnly: true,
256
259
  },
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/extensions/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n createExtension,\n getExtension,\n updateExtension,\n updateExtensionContent,\n} from \"./store.js\";\nimport {\n addExtensionSlotTarget,\n installExtensionSlot,\n uninstallExtensionSlot,\n listExtensionsForSlot,\n listSlotsForExtension,\n} from \"./slots/store.js\";\n\ntype ExtensionPatch = { find: string; replace: string };\n\nexport function createExtensionActionEntries(): Record<string, ActionEntry> {\n return {\n \"create-extension\": {\n tool: {\n description:\n \"Create a sandboxed Alpine.js mini-app extension. Use this when the user asks to create, build, or make an extension/widget/dashboard/calculator. The content must be a self-contained Alpine.js HTML body snippet that can use appAction(), appFetch(), dbQuery(), dbExec(), extensionFetch(), and extensionData.\",\n parameters: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description:\n 'Short display name for the extension. Do not include \"app\" — e.g. name a todo app \"Todos\", a weather app \"Weather\".',\n },\n description: {\n type: \"string\",\n description: \"One-sentence summary of what the extension does.\",\n },\n content: {\n type: \"string\",\n description:\n \"Self-contained Alpine.js HTML body snippet. The iframe body has no padding, so add p-4 or p-6 to the outermost element. Use semantic Tailwind colors (bg-background, text-foreground, bg-primary, etc.) for native theming. Do not include a full app build, React code, or source files.\",\n },\n icon: {\n type: \"string\",\n description: \"Optional icon name or short label.\",\n },\n },\n required: [\"name\", \"content\"],\n },\n },\n run: async (args) => {\n const name = String(args?.name ?? \"\").trim();\n const content = String(args?.content ?? \"\").trim();\n if (!name) return \"Error: name is required.\";\n if (!content) return \"Error: content is required.\";\n\n const extension = await createExtension({\n name,\n description: String(args?.description ?? \"\").trim(),\n content,\n icon: args?.icon ? String(args.icon) : undefined,\n });\n\n return {\n ok: true,\n extension,\n next: `Navigate to /extensions/${extension.id} or use the navigate action with --view=extensions --extensionId=${extension.id}.`,\n };\n },\n },\n\n \"update-extension\": {\n tool: {\n description:\n \"Update an existing sandboxed Alpine.js mini-app extension. Prefer patches for surgical edits; use full content replacement only when necessary.\",\n parameters: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n description: \"Extension id to update.\",\n },\n name: {\n type: \"string\",\n description: \"Optional new display name.\",\n },\n description: {\n type: \"string\",\n description: \"Optional new description.\",\n },\n content: {\n type: \"string\",\n description:\n \"Optional full replacement Alpine.js HTML body snippet.\",\n },\n patches: {\n type: \"string\",\n description:\n 'Optional JSON array of { \"find\": \"...\", \"replace\": \"...\" } patches to apply to the current content.',\n },\n icon: {\n type: \"string\",\n description: \"Optional icon name or short label.\",\n },\n visibility: {\n type: \"string\",\n description: \"Optional sharing visibility.\",\n enum: [\"private\", \"org\", \"public\"],\n },\n },\n required: [\"id\"],\n },\n },\n run: async (args) => {\n const id = String(args?.id ?? \"\").trim();\n if (!id) return \"Error: id is required.\";\n\n let result = null;\n if (args?.content !== undefined || args?.patches !== undefined) {\n const patches = parsePatches((args as any).patches);\n if (args?.patches !== undefined && !patches) {\n return \"Error: patches must be a JSON array of { find, replace } objects.\";\n }\n result = await updateExtensionContent(id, {\n content:\n args?.content !== undefined ? String(args.content) : undefined,\n patches,\n });\n }\n\n const meta: Record<string, string> = {};\n if (args?.name !== undefined) meta.name = String(args.name).trim();\n if (args?.description !== undefined) {\n meta.description = String(args.description).trim();\n }\n if (args?.icon !== undefined) meta.icon = String(args.icon);\n if (args?.visibility !== undefined) {\n meta.visibility = String(args.visibility);\n }\n if (Object.keys(meta).length > 0) {\n result = await updateExtension(id, meta as any);\n }\n\n if (!result) result = await getExtension(id);\n if (!result) return `Error: extension not found: ${id}`;\n return { ok: true, extension: result };\n },\n },\n\n \"add-extension-slot-target\": {\n tool: {\n description:\n 'Declare that an extension can render in a UI extension-point slot of an app (e.g. \"mail.contact-sidebar.bottom\"). Apps drop ExtensionSlot components in their UI; this action registers an extension as installable into one of those slots. Slot IDs follow the convention <app>.<area>.<position>. Caller must have editor access to the extension.',\n parameters: {\n type: \"object\",\n properties: {\n extensionId: { type: \"string\", description: \"Extension id.\" },\n slotId: {\n type: \"string\",\n description:\n 'Slot identifier — e.g. \"mail.contact-sidebar.bottom\".',\n },\n config: {\n type: \"string\",\n description:\n \"Optional JSON string with slot-specific config (defaults, hints, etc.).\",\n },\n },\n required: [\"extensionId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const extensionId = String(args?.extensionId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!extensionId) return \"Error: extensionId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n const row = await addExtensionSlotTarget(\n extensionId,\n slotId,\n args?.config ? String(args.config) : undefined,\n );\n return { ok: true, slot: row };\n },\n },\n\n \"install-extension\": {\n tool: {\n description:\n \"Install an extension as a widget in an extension-point slot for the current user. The extension must already declare the slot via add-extension-slot-target. Per-user installation — only affects the calling user's view. Use after creating an extension that targets a slot, or when the user asks to add an existing widget to a slot.\",\n parameters: {\n type: \"object\",\n properties: {\n extensionId: {\n type: \"string\",\n description: \"Extension id to install.\",\n },\n slotId: {\n type: \"string\",\n description:\n 'Slot identifier — e.g. \"mail.contact-sidebar.bottom\".',\n },\n position: {\n type: \"number\",\n description:\n \"Optional integer position within the slot (lower = earlier). Defaults to end.\",\n },\n config: {\n type: \"string\",\n description:\n \"Optional JSON string with per-install config (overrides, settings).\",\n },\n },\n required: [\"extensionId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const extensionId = String(args?.extensionId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!extensionId) return \"Error: extensionId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n const position =\n args?.position !== undefined && args.position !== null\n ? Number(args.position)\n : undefined;\n const row = await installExtensionSlot(extensionId, slotId, {\n position: Number.isFinite(position as number) ? position : undefined,\n config: args?.config ? String(args.config) : undefined,\n });\n return { ok: true, install: row };\n },\n },\n\n \"uninstall-extension\": {\n tool: {\n description:\n \"Remove an extension from an extension-point slot for the current user. Does not delete the extension itself.\",\n parameters: {\n type: \"object\",\n properties: {\n extensionId: { type: \"string\", description: \"Extension id.\" },\n slotId: { type: \"string\", description: \"Slot identifier.\" },\n },\n required: [\"extensionId\", \"slotId\"],\n },\n },\n run: async (args) => {\n const extensionId = String(args?.extensionId ?? \"\").trim();\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!extensionId) return \"Error: extensionId is required.\";\n if (!slotId) return \"Error: slotId is required.\";\n await uninstallExtensionSlot(extensionId, slotId);\n return { ok: true };\n },\n },\n\n \"list-extensions-for-slot\": {\n tool: {\n description:\n \"List extensions the current user has access to that declare a given extension-point slot. Use to discover what's available to install into a slot the user mentioned.\",\n parameters: {\n type: \"object\",\n properties: {\n slotId: { type: \"string\", description: \"Slot identifier.\" },\n },\n required: [\"slotId\"],\n },\n },\n run: async (args) => {\n const slotId = String(args?.slotId ?? \"\").trim();\n if (!slotId) return \"Error: slotId is required.\";\n return { extensions: await listExtensionsForSlot(slotId) };\n },\n readOnly: true,\n },\n\n \"list-extension-slots\": {\n tool: {\n description:\n \"List the extension-point slots a specific extension declares it can render in. Caller must have viewer access to the extension.\",\n parameters: {\n type: \"object\",\n properties: {\n extensionId: { type: \"string\", description: \"Extension id.\" },\n },\n required: [\"extensionId\"],\n },\n },\n run: async (args) => {\n const extensionId = String(args?.extensionId ?? \"\").trim();\n if (!extensionId) return \"Error: extensionId is required.\";\n return { slots: await listSlotsForExtension(extensionId) };\n },\n readOnly: true,\n },\n };\n}\n\nfunction parsePatches(value: unknown): ExtensionPatch[] | undefined {\n if (value === undefined) return undefined;\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (!Array.isArray(parsed)) return undefined;\n if (\n parsed.some(\n (patch) =>\n !patch ||\n typeof patch.find !== \"string\" ||\n typeof patch.replace !== \"string\",\n )\n ) {\n return undefined;\n }\n return parsed;\n}\n"]}
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Fetch tool — outbound HTTP for automations and agent use.
3
3
  *
4
+ * NOTE: this is an *agent* tool (LLM function call), not an *extension* (the
5
+ * sandboxed Alpine.js mini-app primitive). It lives in this directory because
6
+ * it shares SSRF-safe URL/proxy helpers with the extension iframe proxy.
7
+ *
4
8
  * Supports ${keys.NAME} reference substitution in URL, headers, and body.
5
9
  * Values are resolved server-side AFTER the model emits the tool call —
6
10
  * the raw secret never enters the model's context.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-tool.d.ts","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAchE,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC,CAAC;IACH,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAmM7B"}
@@ -1,12 +1,16 @@
1
1
  /**
2
2
  * Fetch tool — outbound HTTP for automations and agent use.
3
3
  *
4
+ * NOTE: this is an *agent* tool (LLM function call), not an *extension* (the
5
+ * sandboxed Alpine.js mini-app primitive). It lives in this directory because
6
+ * it shares SSRF-safe URL/proxy helpers with the extension iframe proxy.
7
+ *
4
8
  * Supports ${keys.NAME} reference substitution in URL, headers, and body.
5
9
  * Values are resolved server-side AFTER the model emits the tool call —
6
10
  * the raw secret never enters the model's context.
7
11
  */
8
- import { collectSecretValues, MAX_TOOL_PROXY_RESPONSE_SIZE, normalizeToolProxyMethod, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
9
- import { isBlockedToolUrlWithDns } from "./url-safety.js";
12
+ import { collectSecretValues, MAX_EXTENSION_PROXY_RESPONSE_SIZE, normalizeExtensionProxyMethod, readResponseTextWithLimit, redactSecrets, redactString, sanitizeOutboundHeaders, } from "./proxy-security.js";
13
+ import { isBlockedExtensionUrlWithDns } from "./url-safety.js";
10
14
  const DEFAULT_TIMEOUT_MS = 15_000;
11
15
  /**
12
16
  * Create the fetch tool entry for the agent tool registry.
@@ -15,7 +19,7 @@ export function createFetchToolEntry(opts = {}) {
15
19
  return {
16
20
  "web-request": {
17
21
  tool: {
18
- description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered action tools directly (e.g. \`log-meal\`, \`bigquery\`, \`hubspot-deals\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,
22
+ description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered actions directly (e.g. \`log-meal\`, \`bigquery\`, \`hubspot-deals\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,
19
23
  parameters: {
20
24
  type: "object",
21
25
  properties: {
@@ -47,7 +51,7 @@ export function createFetchToolEntry(opts = {}) {
47
51
  run: async (args) => {
48
52
  const startTime = Date.now();
49
53
  const rawUrl = args.url;
50
- const method = normalizeToolProxyMethod(args.method || "GET");
54
+ const method = normalizeExtensionProxyMethod(args.method || "GET");
51
55
  if (!method) {
52
56
  return "Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.";
53
57
  }
@@ -83,7 +87,7 @@ export function createFetchToolEntry(opts = {}) {
83
87
  }
84
88
  const secretValues = collectSecretValues(allSecretValues);
85
89
  // Block SSRF targets regardless of key usage
86
- if (await isBlockedToolUrlWithDns(resolvedUrl)) {
90
+ if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {
87
91
  return `Requests to private/internal addresses are not allowed: "${rawUrl}".`;
88
92
  }
89
93
  // Validate URL against per-key allowlists
@@ -129,7 +133,8 @@ export function createFetchToolEntry(opts = {}) {
129
133
  const redirectUrl = location
130
134
  ? new URL(location, resolvedUrl).href
131
135
  : null;
132
- if (redirectUrl && (await isBlockedToolUrlWithDns(redirectUrl))) {
136
+ if (redirectUrl &&
137
+ (await isBlockedExtensionUrlWithDns(redirectUrl))) {
133
138
  return "Redirect to private/internal address blocked.";
134
139
  }
135
140
  if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {
@@ -142,7 +147,7 @@ export function createFetchToolEntry(opts = {}) {
142
147
  }
143
148
  let body;
144
149
  try {
145
- const result = await readResponseTextWithLimit(response, MAX_TOOL_PROXY_RESPONSE_SIZE);
150
+ const result = await readResponseTextWithLimit(response, MAX_EXTENSION_PROXY_RESPONSE_SIZE);
146
151
  body = result.text;
147
152
  }
148
153
  catch {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-tool.js","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fetch tool — outbound HTTP for automations and agent use.\n *\n * NOTE: this is an *agent* tool (LLM function call), not an *extension* (the\n * sandboxed Alpine.js mini-app primitive). It lives in this directory because\n * it shares SSRF-safe URL/proxy helpers with the extension iframe proxy.\n *\n * Supports ${keys.NAME} reference substitution in URL, headers, and body.\n * Values are resolved server-side AFTER the model emits the tool call —\n * the raw secret never enters the model's context.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n collectSecretValues,\n MAX_EXTENSION_PROXY_RESPONSE_SIZE,\n normalizeExtensionProxyMethod,\n readResponseTextWithLimit,\n redactSecrets,\n redactString,\n sanitizeOutboundHeaders,\n} from \"./proxy-security.js\";\nimport { isBlockedExtensionUrlWithDns } from \"./url-safety.js\";\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\nexport interface FetchToolOptions {\n /** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */\n resolveKeys?: (text: string) => Promise<{\n resolved: string;\n usedKeys: string[];\n secretValues?: string[];\n }>;\n /** Validate URL against per-key allowlists. */\n validateUrl?: (url: string, usedKeys: string[]) => Promise<boolean>;\n}\n\n/**\n * Create the fetch tool entry for the agent tool registry.\n */\nexport function createFetchToolEntry(\n opts: FetchToolOptions = {},\n): Record<string, ActionEntry> {\n return {\n \"web-request\": {\n tool: {\n description: `Make an outbound HTTP request to EXTERNAL APIs, webhooks, and services only. Supports \\${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \\${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered actions directly (e.g. \\`log-meal\\`, \\`bigquery\\`, \\`hubspot-deals\\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,\n parameters: {\n type: \"object\" as const,\n properties: {\n url: {\n type: \"string\",\n description:\n 'Full URL. May contain ${keys.NAME} references, e.g. \"${keys.SLACK_WEBHOOK}\".',\n },\n method: {\n type: \"string\",\n description: \"HTTP method. Default: GET.\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"],\n },\n headers: {\n type: \"string\",\n description:\n 'JSON object of headers. May contain ${keys.NAME} references. Example: \\'{\"Authorization\": \"Bearer ${keys.API_TOKEN}\"}\\'.',\n },\n body: {\n type: \"string\",\n description:\n \"Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.\",\n },\n timeout_ms: {\n type: \"number\",\n description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`,\n },\n },\n required: [\"url\"],\n },\n },\n run: async (args: Record<string, string>) => {\n const startTime = Date.now();\n const rawUrl = args.url;\n const method = normalizeExtensionProxyMethod(args.method || \"GET\");\n if (!method) {\n return \"Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.\";\n }\n const rawHeaders = args.headers || \"{}\";\n const rawBody = args.body;\n const timeoutMs = Math.min(\n Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS,\n 30_000,\n );\n\n // Resolve key references\n let resolvedUrl = rawUrl;\n let resolvedHeaders = rawHeaders;\n let resolvedBody = rawBody;\n const allUsedKeys: string[] = [];\n const allSecretValues: string[] = [];\n\n if (opts.resolveKeys) {\n try {\n const urlResult = await opts.resolveKeys(rawUrl);\n resolvedUrl = urlResult.resolved;\n allUsedKeys.push(...urlResult.usedKeys);\n allSecretValues.push(...(urlResult.secretValues ?? []));\n\n const headerResult = await opts.resolveKeys(rawHeaders);\n resolvedHeaders = headerResult.resolved;\n allUsedKeys.push(...headerResult.usedKeys);\n allSecretValues.push(...(headerResult.secretValues ?? []));\n\n if (rawBody) {\n const bodyResult = await opts.resolveKeys(rawBody);\n resolvedBody = bodyResult.resolved;\n allUsedKeys.push(...bodyResult.usedKeys);\n allSecretValues.push(...(bodyResult.secretValues ?? []));\n }\n } catch (err: any) {\n return `Error resolving key references: ${err?.message ?? err}`;\n }\n }\n const secretValues = collectSecretValues(allSecretValues);\n\n // Block SSRF targets regardless of key usage\n if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {\n return `Requests to private/internal addresses are not allowed: \"${rawUrl}\".`;\n }\n\n // Validate URL against per-key allowlists\n if (opts.validateUrl && allUsedKeys.length > 0) {\n try {\n const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys);\n if (!allowed) {\n return `URL \"${rawUrl}\" is not in the allowlist for the referenced keys. Check your key settings.`;\n }\n } catch (err: any) {\n return `URL validation error: ${err?.message ?? err}`;\n }\n }\n\n // Parse headers\n let headers: Record<string, string>;\n try {\n headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));\n } catch {\n return `Invalid headers JSON: ${rawHeaders}`;\n }\n\n // Make the request\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const fetchOpts: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n redirect: \"manual\",\n };\n if (resolvedBody && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n fetchOpts.body = resolvedBody;\n if (!headers[\"content-type\"] && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n }\n\n const response = await fetch(resolvedUrl, fetchOpts);\n const elapsed = Date.now() - startTime;\n\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n const redirectUrl = location\n ? new URL(location, resolvedUrl).href\n : null;\n if (\n redirectUrl &&\n (await isBlockedExtensionUrlWithDns(redirectUrl))\n ) {\n return \"Redirect to private/internal address blocked.\";\n }\n if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {\n const allowed = await opts.validateUrl(redirectUrl, allUsedKeys);\n if (!allowed) {\n return \"Redirect URL is not in the allowlist for the referenced keys.\";\n }\n }\n return `HTTP ${response.status} ${response.statusText}\\n\\nRedirect: ${\n redirectUrl ? redactString(redirectUrl, secretValues) : \"(none)\"\n }`;\n }\n\n let body: string;\n try {\n const result = await readResponseTextWithLimit(\n response,\n MAX_EXTENSION_PROXY_RESPONSE_SIZE,\n );\n body = result.text;\n } catch {\n body = \"(could not read response body)\";\n }\n body = redactString(body, secretValues);\n\n // Truncate very long responses for the agent\n if (body.length > 8000) {\n body = body.slice(0, 8000) + \"\\n... (truncated)\";\n }\n\n // Audit log\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(\",\") || \"none\"})`,\n );\n\n return `HTTP ${response.status} ${response.statusText}\\n\\n${body}`;\n } catch (err: any) {\n const elapsed = Date.now() - startTime;\n if (err?.name === \"AbortError\") {\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → TIMEOUT (${elapsed}ms)`,\n );\n return `Request timed out after ${timeoutMs}ms.`;\n }\n const message = redactSecrets(\n err?.message ?? String(err),\n secretValues,\n );\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ERROR: ${message} (${elapsed}ms)`,\n );\n return `Request failed: ${message}`;\n } finally {\n clearTimeout(timeout);\n }\n },\n readOnly: true,\n },\n };\n}\n"]}
@@ -0,0 +1,56 @@
1
+ export declare const EXTENSION_IFRAME_CSP = "default-src 'none'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data: blob:; media-src 'self' data: blob:; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self';";
2
+ export declare const EXTENSION_IFRAME_META_CSP: string;
3
+ /**
4
+ * SECURITY — EXTENSION CONTENT IS UNTRUSTED.
5
+ *
6
+ * `${content}` (line ~Body) interpolates raw HTML/JS authored by a user. This
7
+ * file is the boundary between framework-controlled HTML and user-controlled
8
+ * HTML. Two non-negotiable invariants for every change here:
9
+ *
10
+ * 1. The iframe MUST be rendered with a `sandbox` attribute that does NOT
11
+ * include `allow-same-origin`. The viewer (`ExtensionViewer.tsx`,
12
+ * `EmbeddedExtension.tsx`) sets `sandbox="allow-scripts allow-forms"` —
13
+ * and that is the only acceptable shape. Adding `allow-same-origin`
14
+ * would give the extension full DOM access to the parent window via
15
+ * cross-frame script.
16
+ *
17
+ * 2. Every reachable parent action must treat the postMessage payload as
18
+ * hostile. The bridge in `iframe-bridge.ts` enforces a path allowlist,
19
+ * header sanitization, and method allowlist; do not relax those gates
20
+ * for "convenience" in this file or any caller.
21
+ *
22
+ * For the trust model rationale, see audit 05-tools-sandbox.md (C1) and the
23
+ * `extensions` skill. When in doubt, fail closed.
24
+ *
25
+ * BACKWARDS COMPAT — the iframe injects helpers under both their canonical
26
+ * `extension*` names (`extensionFetch`, `extensionData`, `extensionId`,
27
+ * `extensionBinding`) AND legacy `tool*` aliases (`toolFetch`, `toolData`,
28
+ * `toolId`, `toolBinding`) so existing user-authored extension bodies that
29
+ * pre-date the rename keep working. Same for layout opt-ins:
30
+ * `data-extension-layout="full-bleed"` / `data-extension-padding="none"` /
31
+ * class `agent-native-extension-bleed` / CSS var
32
+ * `--agent-native-extension-padding` are canonical; the `data-tool-*`,
33
+ * `agent-native-tool-bleed`, and `--agent-native-tool-padding` variants are
34
+ * accepted as aliases.
35
+ */
36
+ export interface ExtensionRenderBinding {
37
+ /** Email of the user who authored / owns the extension. */
38
+ authorEmail: string;
39
+ /** Email of the user currently viewing/running the extension. */
40
+ viewerEmail: string;
41
+ /** True when viewer === author. */
42
+ isAuthor: boolean;
43
+ /**
44
+ * Resolved role for the viewer ("owner" | "admin" | "editor" | "viewer").
45
+ *
46
+ * TODO(security, audit H4): the host-side bridge does not yet gate any
47
+ * helper based on this value — every viewer gets the same powers as the
48
+ * author. The role is plumbed through so a follow-up PR can constrain
49
+ * `appAction` / `dbExec` / `extensionFetch` for non-author viewers (and
50
+ * eventually require an explicit consent step before running a shared
51
+ * extension, audit C1). For now this is metadata only.
52
+ */
53
+ role: "owner" | "admin" | "editor" | "viewer";
54
+ }
55
+ export declare function buildExtensionHtml(content: string, themeVars: string, isDark: boolean, extensionId?: string, binding?: ExtensionRenderBinding): string;
56
+ //# sourceMappingURL=html-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-shell.d.ts","sourceRoot":"","sources":["../../src/extensions/html-shell.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,8YAC4W,CAAC;AAE9Y,eAAO,MAAM,yBAAyB,QAGrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;;;;;OASG;IACH,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAC/C;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,EACf,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CA4gBR"}