@desktalk/core 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/index.d.ts +3 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +82 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/frontend/assets/frontend-B3QNYf3p.js +413 -0
  7. package/dist/frontend/assets/frontend-B3QNYf3p.js.map +1 -0
  8. package/dist/frontend/assets/frontend-B4aeXn9d.js +416 -0
  9. package/dist/frontend/assets/frontend-B4aeXn9d.js.map +1 -0
  10. package/dist/frontend/assets/frontend-BwyRSlHp.js +6513 -0
  11. package/dist/frontend/assets/frontend-BwyRSlHp.js.map +1 -0
  12. package/dist/frontend/assets/frontend-Dix2OWTT.js +229 -0
  13. package/dist/frontend/assets/frontend-Dix2OWTT.js.map +1 -0
  14. package/dist/frontend/assets/frontend-Dx41dEM9.js +407 -0
  15. package/dist/frontend/assets/frontend-Dx41dEM9.js.map +1 -0
  16. package/dist/frontend/assets/frontend-WpQng8Mt.js +1991 -0
  17. package/dist/frontend/assets/frontend-WpQng8Mt.js.map +1 -0
  18. package/dist/frontend/assets/frontend-rTwBdJbn.js +2123 -0
  19. package/dist/frontend/assets/frontend-rTwBdJbn.js.map +1 -0
  20. package/dist/frontend/assets/highlighted-body-TPN3WLV5-DD4wpkf4.js +2 -0
  21. package/dist/frontend/assets/highlighted-body-TPN3WLV5-DD4wpkf4.js.map +1 -0
  22. package/dist/frontend/assets/index-C5-XUOS7.js +1863 -0
  23. package/dist/frontend/assets/index-C5-XUOS7.js.map +1 -0
  24. package/dist/frontend/assets/index-C_e3_6yE.css +1 -0
  25. package/dist/frontend/index.html +22 -0
  26. package/dist/frontend/pcm-capture-processor.js +65 -0
  27. package/dist/i18n/manifest.json +34 -0
  28. package/dist/i18n/zh-CN.json +7 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +6 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/server/admin-routes.d.ts +14 -0
  34. package/dist/server/admin-routes.d.ts.map +1 -0
  35. package/dist/server/admin-routes.js +118 -0
  36. package/dist/server/admin-routes.js.map +1 -0
  37. package/dist/server/api-routes.d.ts +8 -0
  38. package/dist/server/api-routes.d.ts.map +1 -0
  39. package/dist/server/api-routes.js +203 -0
  40. package/dist/server/api-routes.js.map +1 -0
  41. package/dist/server/auth-routes.d.ts +19 -0
  42. package/dist/server/auth-routes.d.ts.map +1 -0
  43. package/dist/server/auth-routes.js +155 -0
  44. package/dist/server/auth-routes.js.map +1 -0
  45. package/dist/server/dtfs-routes.d.ts +3 -0
  46. package/dist/server/dtfs-routes.d.ts.map +1 -0
  47. package/dist/server/dtfs-routes.js +183 -0
  48. package/dist/server/dtfs-routes.js.map +1 -0
  49. package/dist/server/index.d.ts +15 -0
  50. package/dist/server/index.d.ts.map +1 -0
  51. package/dist/server/index.js +158 -0
  52. package/dist/server/index.js.map +1 -0
  53. package/dist/server/monaco-routes.d.ts +3 -0
  54. package/dist/server/monaco-routes.d.ts.map +1 -0
  55. package/dist/server/monaco-routes.js +45 -0
  56. package/dist/server/monaco-routes.js.map +1 -0
  57. package/dist/server/voice-routes.d.ts +7 -0
  58. package/dist/server/voice-routes.d.ts.map +1 -0
  59. package/dist/server/voice-routes.js +142 -0
  60. package/dist/server/voice-routes.js.map +1 -0
  61. package/dist/server/ws-routes.d.ts +29 -0
  62. package/dist/server/ws-routes.d.ts.map +1 -0
  63. package/dist/server/ws-routes.js +334 -0
  64. package/dist/server/ws-routes.js.map +1 -0
  65. package/dist/services/ai/action-tool.d.ts +9 -0
  66. package/dist/services/ai/action-tool.d.ts.map +1 -0
  67. package/dist/services/ai/action-tool.js +51 -0
  68. package/dist/services/ai/action-tool.js.map +1 -0
  69. package/dist/services/ai/app-tools.d.ts +15 -0
  70. package/dist/services/ai/app-tools.d.ts.map +1 -0
  71. package/dist/services/ai/app-tools.js +119 -0
  72. package/dist/services/ai/app-tools.js.map +1 -0
  73. package/dist/services/ai/chat-service.d.ts +27 -0
  74. package/dist/services/ai/chat-service.d.ts.map +1 -0
  75. package/dist/services/ai/chat-service.js +213 -0
  76. package/dist/services/ai/chat-service.js.map +1 -0
  77. package/dist/services/ai/create-liveapp-tool.d.ts +15 -0
  78. package/dist/services/ai/create-liveapp-tool.d.ts.map +1 -0
  79. package/dist/services/ai/create-liveapp-tool.js +142 -0
  80. package/dist/services/ai/create-liveapp-tool.js.map +1 -0
  81. package/dist/services/ai/desktop-tool.d.ts +27 -0
  82. package/dist/services/ai/desktop-tool.d.ts.map +1 -0
  83. package/dist/services/ai/desktop-tool.js +106 -0
  84. package/dist/services/ai/desktop-tool.js.map +1 -0
  85. package/dist/services/ai/edit-history.d.ts +16 -0
  86. package/dist/services/ai/edit-history.d.ts.map +1 -0
  87. package/dist/services/ai/edit-history.js +137 -0
  88. package/dist/services/ai/edit-history.js.map +1 -0
  89. package/dist/services/ai/edit-tool.d.ts +9 -0
  90. package/dist/services/ai/edit-tool.d.ts.map +1 -0
  91. package/dist/services/ai/edit-tool.js +113 -0
  92. package/dist/services/ai/edit-tool.js.map +1 -0
  93. package/dist/services/ai/generate-html-tool.d.ts +15 -0
  94. package/dist/services/ai/generate-html-tool.d.ts.map +1 -0
  95. package/dist/services/ai/generate-html-tool.js +140 -0
  96. package/dist/services/ai/generate-html-tool.js.map +1 -0
  97. package/dist/services/ai/generate-icon-tool.d.ts +10 -0
  98. package/dist/services/ai/generate-icon-tool.d.ts.map +1 -0
  99. package/dist/services/ai/generate-icon-tool.js +58 -0
  100. package/dist/services/ai/generate-icon-tool.js.map +1 -0
  101. package/dist/services/ai/html-bridge-script.d.ts +2 -0
  102. package/dist/services/ai/html-bridge-script.d.ts.map +1 -0
  103. package/dist/services/ai/html-bridge-script.js +2 -0
  104. package/dist/services/ai/html-bridge-script.js.map +1 -0
  105. package/dist/services/ai/html-guidelines-tool.d.ts +3 -0
  106. package/dist/services/ai/html-guidelines-tool.d.ts.map +1 -0
  107. package/dist/services/ai/html-guidelines-tool.js +157 -0
  108. package/dist/services/ai/html-guidelines-tool.js.map +1 -0
  109. package/dist/services/ai/html-stream-coordinator.d.ts +92 -0
  110. package/dist/services/ai/html-stream-coordinator.d.ts.map +1 -0
  111. package/dist/services/ai/html-stream-coordinator.js +314 -0
  112. package/dist/services/ai/html-stream-coordinator.js.map +1 -0
  113. package/dist/services/ai/html-theme-link.d.ts +9 -0
  114. package/dist/services/ai/html-theme-link.d.ts.map +1 -0
  115. package/dist/services/ai/html-theme-link.js +12 -0
  116. package/dist/services/ai/html-theme-link.js.map +1 -0
  117. package/dist/services/ai/html-ui-script.d.ts +9 -0
  118. package/dist/services/ai/html-ui-script.d.ts.map +1 -0
  119. package/dist/services/ai/html-ui-script.js +9 -0
  120. package/dist/services/ai/html-ui-script.js.map +1 -0
  121. package/dist/services/ai/image-generation-service.d.ts +26 -0
  122. package/dist/services/ai/image-generation-service.d.ts.map +1 -0
  123. package/dist/services/ai/image-generation-service.js +205 -0
  124. package/dist/services/ai/image-generation-service.js.map +1 -0
  125. package/dist/services/ai/manual-pages/desktop-actions.md +36 -0
  126. package/dist/services/ai/manual-pages/desktop-windows.md +24 -0
  127. package/dist/services/ai/manual-pages/dt-badge.md +36 -0
  128. package/dist/services/ai/manual-pages/dt-button.md +38 -0
  129. package/dist/services/ai/manual-pages/dt-card.md +22 -0
  130. package/dist/services/ai/manual-pages/dt-chart.md +261 -0
  131. package/dist/services/ai/manual-pages/dt-divider.md +25 -0
  132. package/dist/services/ai/manual-pages/dt-grid.md +29 -0
  133. package/dist/services/ai/manual-pages/dt-list-view.md +96 -0
  134. package/dist/services/ai/manual-pages/dt-markdown-editor.md +90 -0
  135. package/dist/services/ai/manual-pages/dt-markdown.md +86 -0
  136. package/dist/services/ai/manual-pages/dt-select.md +21 -0
  137. package/dist/services/ai/manual-pages/dt-stack.md +31 -0
  138. package/dist/services/ai/manual-pages/dt-stat.md +43 -0
  139. package/dist/services/ai/manual-pages/dt-table-view.md +120 -0
  140. package/dist/services/ai/manual-pages/dt-tooltip.md +18 -0
  141. package/dist/services/ai/manual-pages/editing-preview.md +25 -0
  142. package/dist/services/ai/manual-pages/html-actions.md +74 -0
  143. package/dist/services/ai/manual-pages/html-bridge.md +187 -0
  144. package/dist/services/ai/manual-pages/html-components.md +66 -0
  145. package/dist/services/ai/manual-pages/html-examples.md +185 -0
  146. package/dist/services/ai/manual-pages/html-layouts.md +128 -0
  147. package/dist/services/ai/manual-pages/html-storage.md +153 -0
  148. package/dist/services/ai/manual-pages/html-tokens.md +26 -0
  149. package/dist/services/ai/manual-pages/index.d.ts +10 -0
  150. package/dist/services/ai/manual-pages/index.d.ts.map +1 -0
  151. package/dist/services/ai/manual-pages/index.js +186 -0
  152. package/dist/services/ai/manual-pages/index.js.map +1 -0
  153. package/dist/services/ai/manual-tool.d.ts +3 -0
  154. package/dist/services/ai/manual-tool.d.ts.map +1 -0
  155. package/dist/services/ai/manual-tool.js +104 -0
  156. package/dist/services/ai/manual-tool.js.map +1 -0
  157. package/dist/services/ai/pi-session-service.d.ts +101 -0
  158. package/dist/services/ai/pi-session-service.d.ts.map +1 -0
  159. package/dist/services/ai/pi-session-service.js +697 -0
  160. package/dist/services/ai/pi-session-service.js.map +1 -0
  161. package/dist/services/ai/providers.d.ts +21 -0
  162. package/dist/services/ai/providers.d.ts.map +1 -0
  163. package/dist/services/ai/providers.js +93 -0
  164. package/dist/services/ai/providers.js.map +1 -0
  165. package/dist/services/ai/redo-edit-tool.d.ts +9 -0
  166. package/dist/services/ai/redo-edit-tool.d.ts.map +1 -0
  167. package/dist/services/ai/redo-edit-tool.js +44 -0
  168. package/dist/services/ai/redo-edit-tool.js.map +1 -0
  169. package/dist/services/ai/system-prompt.d.ts +7 -0
  170. package/dist/services/ai/system-prompt.d.ts.map +1 -0
  171. package/dist/services/ai/system-prompt.js +72 -0
  172. package/dist/services/ai/system-prompt.js.map +1 -0
  173. package/dist/services/ai/undo-edit-tool.d.ts +9 -0
  174. package/dist/services/ai/undo-edit-tool.d.ts.map +1 -0
  175. package/dist/services/ai/undo-edit-tool.js +44 -0
  176. package/dist/services/ai/undo-edit-tool.js.map +1 -0
  177. package/dist/services/ai/window-tools.d.ts +27 -0
  178. package/dist/services/ai/window-tools.d.ts.map +1 -0
  179. package/dist/services/ai/window-tools.js +155 -0
  180. package/dist/services/ai/window-tools.js.map +1 -0
  181. package/dist/services/backend-host.d.ts +10 -0
  182. package/dist/services/backend-host.d.ts.map +1 -0
  183. package/dist/services/backend-host.js +117 -0
  184. package/dist/services/backend-host.js.map +1 -0
  185. package/dist/services/backend-ipc.d.ts +60 -0
  186. package/dist/services/backend-ipc.d.ts.map +1 -0
  187. package/dist/services/backend-ipc.js +2 -0
  188. package/dist/services/backend-ipc.js.map +1 -0
  189. package/dist/services/backend-process-manager.d.ts +60 -0
  190. package/dist/services/backend-process-manager.d.ts.map +1 -0
  191. package/dist/services/backend-process-manager.js +203 -0
  192. package/dist/services/backend-process-manager.js.map +1 -0
  193. package/dist/services/filesystem.d.ts +8 -0
  194. package/dist/services/filesystem.d.ts.map +1 -0
  195. package/dist/services/filesystem.js +94 -0
  196. package/dist/services/filesystem.js.map +1 -0
  197. package/dist/services/i18n.d.ts +28 -0
  198. package/dist/services/i18n.d.ts.map +1 -0
  199. package/dist/services/i18n.js +76 -0
  200. package/dist/services/i18n.js.map +1 -0
  201. package/dist/services/liveapp-icon.d.ts +9 -0
  202. package/dist/services/liveapp-icon.d.ts.map +1 -0
  203. package/dist/services/liveapp-icon.js +28 -0
  204. package/dist/services/liveapp-icon.js.map +1 -0
  205. package/dist/services/liveapps.d.ts +12 -0
  206. package/dist/services/liveapps.d.ts.map +1 -0
  207. package/dist/services/liveapps.js +84 -0
  208. package/dist/services/liveapps.js.map +1 -0
  209. package/dist/services/logger.d.ts +34 -0
  210. package/dist/services/logger.d.ts.map +1 -0
  211. package/dist/services/logger.js +74 -0
  212. package/dist/services/logger.js.map +1 -0
  213. package/dist/services/messaging.d.ts +14 -0
  214. package/dist/services/messaging.d.ts.map +1 -0
  215. package/dist/services/messaging.js +41 -0
  216. package/dist/services/messaging.js.map +1 -0
  217. package/dist/services/miniapp-icon.d.ts +9 -0
  218. package/dist/services/miniapp-icon.d.ts.map +1 -0
  219. package/dist/services/miniapp-icon.js +25 -0
  220. package/dist/services/miniapp-icon.js.map +1 -0
  221. package/dist/services/miniapp-registry.d.ts +73 -0
  222. package/dist/services/miniapp-registry.d.ts.map +1 -0
  223. package/dist/services/miniapp-registry.js +144 -0
  224. package/dist/services/miniapp-registry.js.map +1 -0
  225. package/dist/services/onboarding-config.d.ts +37 -0
  226. package/dist/services/onboarding-config.d.ts.map +1 -0
  227. package/dist/services/onboarding-config.js +76 -0
  228. package/dist/services/onboarding-config.js.map +1 -0
  229. package/dist/services/preferences.d.ts +10 -0
  230. package/dist/services/preferences.d.ts.map +1 -0
  231. package/dist/services/preferences.js +48 -0
  232. package/dist/services/preferences.js.map +1 -0
  233. package/dist/services/proxy-dispatcher.d.ts +18 -0
  234. package/dist/services/proxy-dispatcher.d.ts.map +1 -0
  235. package/dist/services/proxy-dispatcher.js +33 -0
  236. package/dist/services/proxy-dispatcher.js.map +1 -0
  237. package/dist/services/storage.d.ts +7 -0
  238. package/dist/services/storage.d.ts.map +1 -0
  239. package/dist/services/storage.js +55 -0
  240. package/dist/services/storage.js.map +1 -0
  241. package/dist/services/theme-css.d.ts +21 -0
  242. package/dist/services/theme-css.d.ts.map +1 -0
  243. package/dist/services/theme-css.js +213 -0
  244. package/dist/services/theme-css.js.map +1 -0
  245. package/dist/services/user-db.d.ts +69 -0
  246. package/dist/services/user-db.d.ts.map +1 -0
  247. package/dist/services/user-db.js +175 -0
  248. package/dist/services/user-db.js.map +1 -0
  249. package/dist/services/voice/audio-format.d.ts +5 -0
  250. package/dist/services/voice/audio-format.d.ts.map +1 -0
  251. package/dist/services/voice/audio-format.js +27 -0
  252. package/dist/services/voice/audio-format.js.map +1 -0
  253. package/dist/services/voice/azure-openai-whisper-adapter.d.ts +23 -0
  254. package/dist/services/voice/azure-openai-whisper-adapter.d.ts.map +1 -0
  255. package/dist/services/voice/azure-openai-whisper-adapter.js +60 -0
  256. package/dist/services/voice/azure-openai-whisper-adapter.js.map +1 -0
  257. package/dist/services/voice/openai-whisper-adapter.d.ts +22 -0
  258. package/dist/services/voice/openai-whisper-adapter.d.ts.map +1 -0
  259. package/dist/services/voice/openai-whisper-adapter.js +61 -0
  260. package/dist/services/voice/openai-whisper-adapter.js.map +1 -0
  261. package/dist/services/voice/stt-adapter.d.ts +31 -0
  262. package/dist/services/voice/stt-adapter.d.ts.map +1 -0
  263. package/dist/services/voice/stt-adapter.js +8 -0
  264. package/dist/services/voice/stt-adapter.js.map +1 -0
  265. package/dist/services/voice/vad-segmenter.d.ts +68 -0
  266. package/dist/services/voice/vad-segmenter.d.ts.map +1 -0
  267. package/dist/services/voice/vad-segmenter.js +159 -0
  268. package/dist/services/voice/vad-segmenter.js.map +1 -0
  269. package/dist/services/voice/voice-session.d.ts +54 -0
  270. package/dist/services/voice/voice-session.d.ts.map +1 -0
  271. package/dist/services/voice/voice-session.js +137 -0
  272. package/dist/services/voice/voice-session.js.map +1 -0
  273. package/dist/services/window-manager.d.ts +94 -0
  274. package/dist/services/window-manager.d.ts.map +1 -0
  275. package/dist/services/window-manager.js +282 -0
  276. package/dist/services/window-manager.js.map +1 -0
  277. package/dist/services/workspace.d.ts +51 -0
  278. package/dist/services/workspace.d.ts.map +1 -0
  279. package/dist/services/workspace.js +144 -0
  280. package/dist/services/workspace.js.map +1 -0
  281. package/package.json +89 -0
@@ -0,0 +1,106 @@
1
+ import { StringEnum } from '@mariozechner/pi-ai';
2
+ import { Type } from '@sinclair/typebox';
3
+ const desktopSchema = Type.Object({
4
+ action: StringEnum(['list', 'focus', 'maximize', 'close', 'open']),
5
+ windowId: Type.Optional(Type.String({ description: 'Target window ID' })),
6
+ miniAppId: Type.Optional(Type.String({ description: 'MiniApp ID to open' })),
7
+ args: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
8
+ description: 'Optional launch arguments forwarded to the MiniApp frontend on open',
9
+ })),
10
+ });
11
+ function stringify(data) {
12
+ return JSON.stringify(data, null, 2);
13
+ }
14
+ function requireValue(value, message) {
15
+ if (value === undefined || value === null || value === '') {
16
+ throw new Error(message);
17
+ }
18
+ return value;
19
+ }
20
+ export function createDesktopTool(options) {
21
+ const { windowManager, getMiniApps, getLiveApps, activateMiniApp, sendAiCommand } = options;
22
+ return {
23
+ name: 'desktop',
24
+ label: 'Desktop',
25
+ description: 'Manage DeskTalk windows: list open windows, open MiniApps, focus, maximize, or close windows.',
26
+ promptSnippet: 'Manage DeskTalk desktop windows (list, open, focus, maximize, close).',
27
+ promptGuidelines: [
28
+ 'Use action="list" to get the latest window IDs and desktop state.',
29
+ 'Use action="open" with miniAppId to launch a MiniApp. Pass args to provide initial context (e.g. { path: "photos/cat.png" } for Preview). If a window with the same miniAppId and shallow-equal args already exists, it will be focused instead of opening a duplicate.',
30
+ 'Call `read_manual` with `page: "desktop/windows"` when you need the full DeskTalk window-management reference.',
31
+ ],
32
+ parameters: desktopSchema,
33
+ async execute(_toolCallId, params) {
34
+ const input = params;
35
+ switch (input.action) {
36
+ case 'list': {
37
+ const windows = windowManager.getWindows();
38
+ const focusedWindow = windowManager.getFocusedWindow();
39
+ const focusedWindowActions = focusedWindow
40
+ ? windowManager.getWindowActions(focusedWindow.id)
41
+ : [];
42
+ const payload = {
43
+ windows,
44
+ focusedWindowActions,
45
+ availableMiniApps: getMiniApps().map((m) => ({ id: m.id, name: m.name })),
46
+ availableLiveApps: getLiveApps(),
47
+ };
48
+ return {
49
+ content: [{ type: 'text', text: stringify(payload) }],
50
+ details: payload,
51
+ };
52
+ }
53
+ case 'open': {
54
+ const miniAppId = requireValue(input.miniAppId, 'miniAppId is required for action="open"');
55
+ const manifest = getMiniApps().find((m) => m.id === miniAppId);
56
+ if (!manifest)
57
+ throw new Error(`Unknown MiniApp: ${miniAppId}`);
58
+ activateMiniApp(miniAppId);
59
+ const commandResult = await sendAiCommand({
60
+ action: 'open',
61
+ miniAppId,
62
+ title: manifest.name,
63
+ args: input.args,
64
+ });
65
+ if (!commandResult.ok)
66
+ throw new Error(commandResult.error ?? 'Failed to open window');
67
+ const result = {
68
+ ok: true,
69
+ action: input.action,
70
+ windowId: commandResult.windowId,
71
+ miniAppId,
72
+ title: manifest.name,
73
+ };
74
+ return { content: [{ type: 'text', text: stringify(result) }], details: result };
75
+ }
76
+ case 'focus': {
77
+ const windowId = requireValue(input.windowId, 'windowId is required for action="focus"');
78
+ const commandResult = await sendAiCommand({ action: 'focus', windowId });
79
+ if (!commandResult.ok)
80
+ throw new Error(commandResult.error ?? 'Failed to focus window');
81
+ const result = { ok: true, action: input.action, windowId };
82
+ return { content: [{ type: 'text', text: stringify(result) }], details: result };
83
+ }
84
+ case 'maximize': {
85
+ const windowId = requireValue(input.windowId, 'windowId is required for action="maximize"');
86
+ const commandResult = await sendAiCommand({ action: 'maximize', windowId });
87
+ if (!commandResult.ok)
88
+ throw new Error(commandResult.error ?? 'Failed to maximize window');
89
+ const result = { ok: true, action: input.action, windowId };
90
+ return { content: [{ type: 'text', text: stringify(result) }], details: result };
91
+ }
92
+ case 'close': {
93
+ const windowId = requireValue(input.windowId, 'windowId is required for action="close"');
94
+ const commandResult = await sendAiCommand({ action: 'close', windowId });
95
+ if (!commandResult.ok)
96
+ throw new Error(commandResult.error ?? 'Failed to close window');
97
+ const result = { ok: true, action: input.action, windowId };
98
+ return { content: [{ type: 'text', text: stringify(result) }], details: result };
99
+ }
100
+ default:
101
+ throw new Error(`Unsupported desktop action: ${String(input.action)}`);
102
+ }
103
+ },
104
+ };
105
+ }
106
+ //# sourceMappingURL=desktop-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-tool.js","sourceRoot":"","sources":["../../../src/services/ai/desktop-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export type ManagedPathResolver = (inputPath: string) => string;
2
+ export declare function isPathWithinRoot(rootDir: string, candidatePath: string): boolean;
3
+ export declare function createManagedPathResolver(allowedRoots: string[]): ManagedPathResolver;
4
+ export declare function getHistoryFilePath(filePath: string): string;
5
+ export declare class EditHistory {
6
+ private readonly resolvePath;
7
+ constructor(resolvePath: ManagedPathResolver);
8
+ resolveManagedPath(inputPath: string): string;
9
+ recordEdit(filePath: string, previousContent: string, nextContent: string): void;
10
+ undo(filePath: string): string | null;
11
+ redo(filePath: string): string | null;
12
+ private loadOrCreateHistory;
13
+ private loadHistory;
14
+ private writeHistory;
15
+ }
16
+ //# sourceMappingURL=edit-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-history.d.ts","sourceRoot":"","sources":["../../../src/services/ai/edit-history.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;AAEhE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAKhF;AAED,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,mBAAmB,CA2BrF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3D;AAqDD,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,mBAAmB;IAE7D,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI7C,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IA0BhF,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAYrC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAYrC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,YAAY;CAKrB"}
@@ -0,0 +1,137 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
3
+ export function isPathWithinRoot(rootDir, candidatePath) {
4
+ const normalizedRoot = resolve(rootDir);
5
+ const normalizedCandidate = resolve(candidatePath);
6
+ const rel = relative(normalizedRoot, normalizedCandidate);
7
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
8
+ }
9
+ export function createManagedPathResolver(allowedRoots) {
10
+ const roots = allowedRoots.map((root) => resolve(root));
11
+ if (roots.length === 0) {
12
+ throw new Error('At least one allowed root is required.');
13
+ }
14
+ return (inputPath) => {
15
+ const trimmedPath = inputPath.trim();
16
+ if (!trimmedPath) {
17
+ throw new Error('Path is required.');
18
+ }
19
+ if (isAbsolute(trimmedPath)) {
20
+ const absolutePath = resolve(trimmedPath);
21
+ if (!roots.some((root) => isPathWithinRoot(root, absolutePath))) {
22
+ throw new Error(`Path is outside managed directories: ${inputPath}`);
23
+ }
24
+ return absolutePath;
25
+ }
26
+ const absolutePath = resolve(roots[0], trimmedPath);
27
+ if (!isPathWithinRoot(roots[0], absolutePath)) {
28
+ throw new Error(`Path is outside managed directories: ${inputPath}`);
29
+ }
30
+ return absolutePath;
31
+ };
32
+ }
33
+ export function getHistoryFilePath(filePath) {
34
+ return join(dirname(filePath), `.${basename(filePath)}.history.jsonl`);
35
+ }
36
+ function parseHistory(raw, historyPath) {
37
+ const lines = raw
38
+ .split('\n')
39
+ .map((line) => line.trim())
40
+ .filter((line) => line.length > 0);
41
+ const versions = [];
42
+ let pointer = null;
43
+ for (const line of lines) {
44
+ const parsed = JSON.parse(line);
45
+ if (typeof parsed.pointer === 'number') {
46
+ pointer = parsed.pointer;
47
+ continue;
48
+ }
49
+ if (typeof parsed.v !== 'number' ||
50
+ typeof parsed.ts !== 'number' ||
51
+ typeof parsed.content !== 'string') {
52
+ throw new Error(`Malformed edit history entry in ${historyPath}`);
53
+ }
54
+ versions.push({ v: parsed.v, ts: parsed.ts, content: parsed.content });
55
+ }
56
+ if (versions.length === 0) {
57
+ throw new Error(`Edit history is empty: ${historyPath}`);
58
+ }
59
+ const resolvedPointer = pointer ?? versions[versions.length - 1].v;
60
+ if (resolvedPointer < 1 || resolvedPointer > versions.length) {
61
+ throw new Error(`Edit history pointer is invalid in ${historyPath}`);
62
+ }
63
+ return { versions, pointer: resolvedPointer };
64
+ }
65
+ function serializeHistory(history) {
66
+ const versionLines = history.versions.map((entry, index) => JSON.stringify({ v: index + 1, ts: entry.ts, content: entry.content }));
67
+ return [...versionLines, JSON.stringify({ pointer: history.pointer })].join('\n') + '\n';
68
+ }
69
+ export class EditHistory {
70
+ resolvePath;
71
+ constructor(resolvePath) {
72
+ this.resolvePath = resolvePath;
73
+ }
74
+ resolveManagedPath(inputPath) {
75
+ return this.resolvePath(inputPath);
76
+ }
77
+ recordEdit(filePath, previousContent, nextContent) {
78
+ const absolutePath = this.resolvePath(filePath);
79
+ const history = this.loadOrCreateHistory(absolutePath, previousContent);
80
+ const currentContent = history.versions[history.pointer - 1]?.content;
81
+ history.versions = history.versions.slice(0, history.pointer);
82
+ if (currentContent !== previousContent) {
83
+ history.versions.push({
84
+ v: history.versions.length + 1,
85
+ ts: Date.now(),
86
+ content: previousContent,
87
+ });
88
+ history.pointer = history.versions.length;
89
+ }
90
+ history.versions.push({
91
+ v: history.versions.length + 1,
92
+ ts: Date.now(),
93
+ content: nextContent,
94
+ });
95
+ history.pointer = history.versions.length;
96
+ this.writeHistory(absolutePath, history);
97
+ }
98
+ undo(filePath) {
99
+ const absolutePath = this.resolvePath(filePath);
100
+ const history = this.loadHistory(absolutePath);
101
+ if (!history || history.pointer <= 1) {
102
+ return null;
103
+ }
104
+ history.pointer -= 1;
105
+ this.writeHistory(absolutePath, history);
106
+ return history.versions[history.pointer - 1]?.content ?? null;
107
+ }
108
+ redo(filePath) {
109
+ const absolutePath = this.resolvePath(filePath);
110
+ const history = this.loadHistory(absolutePath);
111
+ if (!history || history.pointer >= history.versions.length) {
112
+ return null;
113
+ }
114
+ history.pointer += 1;
115
+ this.writeHistory(absolutePath, history);
116
+ return history.versions[history.pointer - 1]?.content ?? null;
117
+ }
118
+ loadOrCreateHistory(filePath, initialContent) {
119
+ return (this.loadHistory(filePath) ?? {
120
+ versions: [{ v: 1, ts: Date.now(), content: initialContent }],
121
+ pointer: 1,
122
+ });
123
+ }
124
+ loadHistory(filePath) {
125
+ const historyPath = getHistoryFilePath(filePath);
126
+ if (!existsSync(historyPath)) {
127
+ return null;
128
+ }
129
+ return parseHistory(readFileSync(historyPath, 'utf-8'), historyPath);
130
+ }
131
+ writeHistory(filePath, history) {
132
+ const historyPath = getHistoryFilePath(filePath);
133
+ mkdirSync(dirname(historyPath), { recursive: true });
134
+ writeFileSync(historyPath, serializeHistory(history), 'utf-8');
135
+ }
136
+ }
137
+ //# sourceMappingURL=edit-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-history.js","sourceRoot":"","sources":["../../../src/services/ai/edit-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
2
+ import { EditHistory, type ManagedPathResolver } from './edit-history.js';
3
+ interface EditToolOptions {
4
+ editHistory: EditHistory;
5
+ resolvePath: ManagedPathResolver;
6
+ }
7
+ export declare function createEditTool(options: EditToolOptions): ToolDefinition;
8
+ export {};
9
+ //# sourceMappingURL=edit-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-tool.d.ts","sourceRoot":"","sources":["../../../src/services/ai/edit-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAcvE,UAAU,eAAe;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AA4DD,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CA6EvE"}
@@ -0,0 +1,113 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { Type } from '@sinclair/typebox';
3
+ import { broadcastEvent } from '../messaging.js';
4
+ const editSchema = Type.Object({
5
+ path: Type.String({ description: 'Path to the file to edit. Absolute or user-home-relative.' }),
6
+ oldText: Type.String({ description: 'Exact text to replace. Multi-line text is supported.' }),
7
+ newText: Type.String({ description: 'Replacement text. Multi-line text is supported.' }),
8
+ });
9
+ function countOccurrences(content, needle) {
10
+ if (!needle) {
11
+ return 0;
12
+ }
13
+ let count = 0;
14
+ let searchIndex = 0;
15
+ while (true) {
16
+ const matchIndex = content.indexOf(needle, searchIndex);
17
+ if (matchIndex === -1) {
18
+ return count;
19
+ }
20
+ count += 1;
21
+ searchIndex = matchIndex + needle.length;
22
+ }
23
+ }
24
+ function computeDiff(oldContent, newContent, oldText, newText, matchIndex) {
25
+ const linesBefore = oldContent.substring(0, matchIndex).split('\n');
26
+ const firstChangedLine = linesBefore.length;
27
+ const oldLines = oldText.split('\n');
28
+ const newLines = newText.split('\n');
29
+ const allOldLines = oldContent.split('\n');
30
+ const allNewLines = newContent.split('\n');
31
+ const contextStart = Math.max(0, firstChangedLine - 4);
32
+ const lines = [];
33
+ lines.push(`@@ -${firstChangedLine},${oldLines.length} +${firstChangedLine},${newLines.length} @@`);
34
+ for (let index = contextStart; index < firstChangedLine - 1; index += 1) {
35
+ lines.push(` ${allOldLines[index]}`);
36
+ }
37
+ for (const line of oldLines) {
38
+ lines.push(`-${line}`);
39
+ }
40
+ for (const line of newLines) {
41
+ lines.push(`+${line}`);
42
+ }
43
+ const afterStart = firstChangedLine - 1 + oldLines.length;
44
+ const afterEnd = Math.min(allNewLines.length, afterStart + 3);
45
+ for (let index = afterStart; index < afterEnd; index += 1) {
46
+ if (allNewLines[index] !== undefined) {
47
+ lines.push(` ${allNewLines[index]}`);
48
+ }
49
+ }
50
+ return { diff: lines.join('\n'), firstChangedLine };
51
+ }
52
+ export function createEditTool(options) {
53
+ const { editHistory, resolvePath } = options;
54
+ return {
55
+ name: 'edit',
56
+ label: 'Edit File',
57
+ description: 'Edit a managed file by replacing one exact text match with new text. Supports multi-line replacements and persistent undo/redo history.',
58
+ promptSnippet: 'Edit a managed file with exact text replacement.',
59
+ promptGuidelines: [
60
+ 'Use this after discovering the target file path from a Preview Get State action or another trusted source.',
61
+ 'Read the file first so you can provide an exact oldText match.',
62
+ 'oldText must match exactly once or the tool will fail.',
63
+ 'Call `read_manual` with `page: "editing/preview"` when editing an existing Preview document.',
64
+ ],
65
+ parameters: editSchema,
66
+ async execute(_toolCallId, params) {
67
+ const input = params;
68
+ if (!input.oldText) {
69
+ throw new Error('oldText must not be empty.');
70
+ }
71
+ const absolutePath = resolvePath(input.path);
72
+ if (!existsSync(absolutePath)) {
73
+ throw new Error(`File not found: ${input.path}`);
74
+ }
75
+ const currentContent = readFileSync(absolutePath, 'utf-8');
76
+ const occurrenceCount = countOccurrences(currentContent, input.oldText);
77
+ if (occurrenceCount === 0) {
78
+ throw new Error('oldText was not found in the file.');
79
+ }
80
+ if (occurrenceCount > 1) {
81
+ throw new Error(`oldText matched ${occurrenceCount} times; provide a unique match.`);
82
+ }
83
+ const matchIndex = currentContent.indexOf(input.oldText);
84
+ const nextContent = currentContent.slice(0, matchIndex) +
85
+ input.newText +
86
+ currentContent.slice(matchIndex + input.oldText.length);
87
+ editHistory.recordEdit(absolutePath, currentContent, nextContent);
88
+ writeFileSync(absolutePath, nextContent, 'utf-8');
89
+ broadcastEvent('preview', 'preview.file-changed', {
90
+ filePath: absolutePath,
91
+ content: nextContent,
92
+ });
93
+ if (absolutePath.replace(/\\/g, '/').includes('/.data/liveapps/')) {
94
+ broadcastEvent('preview', 'liveapps.changed', {
95
+ path: absolutePath,
96
+ reason: 'edited',
97
+ });
98
+ }
99
+ const { diff, firstChangedLine } = computeDiff(currentContent, nextContent, input.oldText, input.newText, matchIndex);
100
+ const payload = {
101
+ ok: true,
102
+ path: absolutePath,
103
+ firstChangedLine,
104
+ diff,
105
+ };
106
+ return {
107
+ content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
108
+ details: payload,
109
+ };
110
+ },
111
+ };
112
+ }
113
+ //# sourceMappingURL=edit-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-tool.js","sourceRoot":"","sources":["../../../src/services/ai/edit-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAElE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAC7B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC;IAC/F,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;IAC7F,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;CACzF,CAAC,CAAC;AAaH,SAAS,gBAAgB,CAAC,OAAe,EAAE,MAAc;IACvD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACxD,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,IAAI,CAAC,CAAC;QACX,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAClB,UAAkB,EAClB,UAAkB,EAClB,OAAe,EACf,OAAe,EACf,UAAkB;IAElB,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,OAAO,gBAAgB,IAAI,QAAQ,CAAC,MAAM,KAAK,gBAAgB,IAAI,QAAQ,CAAC,MAAM,KAAK,CACxF,CAAC;IAEF,KAAK,IAAI,KAAK,GAAG,YAAY,EAAE,KAAK,GAAG,gBAAgB,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,UAAU,GAAG,gBAAgB,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;IAC9D,KAAK,IAAI,KAAK,GAAG,UAAU,EAAE,KAAK,GAAG,QAAQ,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1D,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAE7C,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,WAAW;QAClB,WAAW,EACT,yIAAyI;QAC3I,aAAa,EAAE,kDAAkD;QACjE,gBAAgB,EAAE;YAChB,4GAA4G;YAC5G,gEAAgE;YAChE,wDAAwD;YACxD,8FAA8F;SAC/F;QACD,UAAU,EAAE,UAAU;QACtB,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM;YAC/B,MAAM,KAAK,GAAG,MAAoB,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAChD,CAAC;YAED,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,eAAe,GAAG,gBAAgB,CAAC,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACxE,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACxD,CAAC;YACD,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,mBAAmB,eAAe,iCAAiC,CAAC,CAAC;YACvF,CAAC;YAED,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACzD,MAAM,WAAW,GACf,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;gBACnC,KAAK,CAAC,OAAO;gBACb,cAAc,CAAC,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE1D,WAAW,CAAC,UAAU,CAAC,YAAY,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;YAClE,aAAa,CAAC,YAAY,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAElD,cAAc,CAAC,SAAS,EAAE,sBAAsB,EAAE;gBAChD,QAAQ,EAAE,YAAY;gBACtB,OAAO,EAAE,WAAW;aACrB,CAAC,CAAC;YACH,IAAI,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAClE,cAAc,CAAC,SAAS,EAAE,kBAAkB,EAAE;oBAC5C,IAAI,EAAE,YAAY;oBAClB,MAAM,EAAE,QAAQ;iBACjB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,WAAW,CAC5C,cAAc,EACd,WAAW,EACX,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,OAAO,EACb,UAAU,CACX,CAAC;YAEF,MAAM,OAAO,GAAG;gBACd,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,YAAY;gBAClB,gBAAgB;gBAChB,IAAI;aACL,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACnE,OAAO,EAAE,OAAO;aACjB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
2
+ import type { SendAiCommand } from './desktop-tool.js';
3
+ import type { HtmlStreamCoordinator } from './html-stream-coordinator.js';
4
+ type PreferenceValue = string | number | boolean;
5
+ type PreferenceReader = (key: string) => PreferenceValue | undefined | Promise<PreferenceValue | undefined>;
6
+ interface GenerateHtmlToolOptions {
7
+ sendAiCommand: SendAiCommand;
8
+ activateMiniApp: (miniAppId: string) => void;
9
+ getPreference: PreferenceReader;
10
+ /** Shared coordinator for streaming HTML content from toolcall_delta events. */
11
+ streamCoordinator: HtmlStreamCoordinator;
12
+ }
13
+ export declare function createGenerateHtmlTool(options: GenerateHtmlToolOptions): ToolDefinition;
14
+ export {};
15
+ //# sourceMappingURL=generate-html-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-html-tool.d.ts","sourceRoot":"","sources":["../../../src/services/ai/generate-html-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAOpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAcvE,KAAK,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACjD,KAAK,gBAAgB,GAAG,CACtB,GAAG,EAAE,MAAM,KACR,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;AAExE,UAAU,uBAAuB;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,gBAAgB,CAAC;IAChC,gFAAgF;IAChF,iBAAiB,EAAE,qBAAqB,CAAC;CAC1C;AAsDD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,cAAc,CAmGvF"}
@@ -0,0 +1,140 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { broadcastEvent } from '../messaging.js';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { DEFAULT_THEME_PREFERENCES } from '../theme-css.js';
5
+ import { createHtmlBridgeScript } from './html-bridge-script.js';
6
+ import { createThemeLinkTag } from './html-theme-link.js';
7
+ import { UI_BUNDLE_SCRIPT_TAG } from './html-ui-script.js';
8
+ const generateHtmlSchema = Type.Object({
9
+ title: Type.String({ description: 'Window title for the generated HTML preview' }),
10
+ content: Type.String({
11
+ description: 'Complete HTML content to display. Must be a self-contained HTML document.',
12
+ }),
13
+ });
14
+ let streamCounter = 0;
15
+ function nextStreamId() {
16
+ return `html-stream-${++streamCounter}-${Date.now()}`;
17
+ }
18
+ /**
19
+ * Read the current theme preferences from the preference store.
20
+ */
21
+ async function readThemePreferences(getPreference) {
22
+ const accentColor = await getPreference('general.accentColor');
23
+ const theme = await getPreference('general.theme');
24
+ return {
25
+ accentColor: typeof accentColor === 'string' && accentColor
26
+ ? accentColor
27
+ : DEFAULT_THEME_PREFERENCES.accentColor,
28
+ theme: theme === 'light' ? 'light' : DEFAULT_THEME_PREFERENCES.theme,
29
+ };
30
+ }
31
+ /**
32
+ * Inject the DeskTalk theme CSS `<link>` tag into AI-generated HTML.
33
+ *
34
+ * Strategy: insert a `<link>` right after the opening `<head>` tag.
35
+ * If there is no `<head>`, prepend it before the content.
36
+ */
37
+ function injectThemeIntoHtml(html, themePreferences) {
38
+ const themeLink = createThemeLinkTag(themePreferences.accentColor, themePreferences.theme);
39
+ // Try to inject after <head> or <head ...>
40
+ const headMatch = html.match(/<head(\s[^>]*)?>|<head>/i);
41
+ if (headMatch && headMatch.index !== undefined) {
42
+ const insertPos = headMatch.index + headMatch[0].length;
43
+ return html.slice(0, insertPos) + '\n' + themeLink + '\n' + html.slice(insertPos);
44
+ }
45
+ // No <head> tag found — prepend the link before the entire content
46
+ return themeLink + '\n' + html;
47
+ }
48
+ function injectIntoHtmlHead(html, snippet) {
49
+ const headMatch = html.match(/<head(\s[^>]*)?>|<head>/i);
50
+ if (headMatch && headMatch.index !== undefined) {
51
+ const insertPos = headMatch.index + headMatch[0].length;
52
+ return html.slice(0, insertPos) + '\n' + snippet + '\n' + html.slice(insertPos);
53
+ }
54
+ return snippet + '\n' + html;
55
+ }
56
+ export function createGenerateHtmlTool(options) {
57
+ const { sendAiCommand, activateMiniApp, getPreference, streamCoordinator } = options;
58
+ return {
59
+ name: 'generate_html',
60
+ label: 'Generate HTML',
61
+ description: 'Generate visual HTML content and display it in a Preview window. Use this when the user asks to show something visually — charts, diagrams, styled layouts, interactive widgets, etc.',
62
+ promptSnippet: 'Generate and display HTML content in a Preview window.',
63
+ promptGuidelines: [
64
+ 'Use this tool when the user asks you to show, visualize, display, or render something visually.',
65
+ 'Provide a complete, self-contained HTML document including <html>, <head>, and <body> tags.',
66
+ 'Follow DeskTalk HTML manual guidance for `<dt-card>` usage, layout rules, and pre-styled typography.',
67
+ 'Generated previews automatically receive a `window.DeskTalk` bridge for reading safe desktop state and running constrained commands.',
68
+ 'The bridge exposes `exec` / `execute` — both accept either a shell string (`window.DeskTalk.exec("ls -la")`) or explicit arguments (`window.DeskTalk.exec("ls", ["-la"])`).',
69
+ 'Call `read_manual` with pages such as `html/tokens`, `html/components`, `html/layouts`, `html/bridge`, or `html/examples` when you need the full DeskTalk reference.',
70
+ ],
71
+ parameters: generateHtmlSchema,
72
+ async execute(_toolCallId, params) {
73
+ const input = params;
74
+ // ── Streaming path ──────────────────────────────────────────────
75
+ // If the coordinator already streamed content via toolcall_delta,
76
+ // finalize the stream with any remaining content.
77
+ const activeSession = streamCoordinator.getActiveSession();
78
+ if (activeSession && activeSession.state === 'streaming' && activeSession.windowOpened) {
79
+ const session = await streamCoordinator.finalize(input.content);
80
+ if (session) {
81
+ const result = {
82
+ ok: true,
83
+ windowId: session.windowId,
84
+ title: input.title,
85
+ streamId: session.streamId,
86
+ contentLength: input.content.length,
87
+ streamed: true,
88
+ };
89
+ return {
90
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
91
+ details: result,
92
+ };
93
+ }
94
+ }
95
+ // ── Fallback: non-streaming path ────────────────────────────────
96
+ // Abort any partially-started session that didn't fully open.
97
+ streamCoordinator.abort();
98
+ const streamId = nextStreamId();
99
+ const bridgeToken = randomUUID();
100
+ // Read current theme preferences and inject into the HTML
101
+ const themePreferences = await readThemePreferences(getPreference);
102
+ const themedHtml = injectThemeIntoHtml(input.content, themePreferences);
103
+ const bridgedHtml = injectIntoHtmlHead(themedHtml, UI_BUNDLE_SCRIPT_TAG + '\n' + createHtmlBridgeScript(streamId, bridgeToken));
104
+ // Ensure the preview MiniApp is activated
105
+ activateMiniApp('preview');
106
+ // Open a Preview window in stream mode
107
+ const commandResult = await sendAiCommand({
108
+ action: 'open',
109
+ miniAppId: 'preview',
110
+ title: input.title,
111
+ args: { streamId, title: input.title, bridgeToken },
112
+ });
113
+ if (!commandResult.ok) {
114
+ throw new Error(commandResult.error ?? 'Failed to open Preview window');
115
+ }
116
+ // Small delay to let the frontend mount and register event listeners
117
+ await new Promise((resolve) => setTimeout(resolve, 100));
118
+ // Broadcast the themed HTML content as a single chunk
119
+ broadcastEvent('preview', 'preview.html-chunk', {
120
+ streamId,
121
+ chunk: bridgedHtml,
122
+ });
123
+ // Signal that streaming is complete
124
+ broadcastEvent('preview', 'preview.html-done', { streamId, html: input.content });
125
+ const result = {
126
+ ok: true,
127
+ windowId: commandResult.windowId,
128
+ title: input.title,
129
+ streamId,
130
+ contentLength: bridgedHtml.length,
131
+ streamed: false,
132
+ };
133
+ return {
134
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
135
+ details: result,
136
+ };
137
+ },
138
+ };
139
+ }
140
+ //# sourceMappingURL=generate-html-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-html-tool.js","sourceRoot":"","sources":["../../../src/services/ai/generate-html-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
2
+ import { ImageGenerationService } from './image-generation-service.js';
3
+ interface GenerateIconToolOptions {
4
+ imageGenerationService: ImageGenerationService;
5
+ getCurrentUsername: () => string;
6
+ workspaceDataDir: string;
7
+ }
8
+ export declare function createGenerateIconTool(options: GenerateIconToolOptions): ToolDefinition;
9
+ export {};
10
+ //# sourceMappingURL=generate-icon-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-icon-tool.d.ts","sourceRoot":"","sources":["../../../src/services/ai/generate-icon-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAYpE,UAAU,uBAAuB;IAC/B,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,cAAc,CAyDvF"}