@tutti-os/agent-gui 0.0.3

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 (112) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +56 -0
  3. package/dist/agent-message-center/index.d.ts +218 -0
  4. package/dist/agent-message-center/index.js +1962 -0
  5. package/dist/agent-message-center/index.js.map +1 -0
  6. package/dist/agent-rich-text-at-provider.d.ts +49 -0
  7. package/dist/agent-rich-text-at-provider.js +7 -0
  8. package/dist/agent-rich-text-at-provider.js.map +1 -0
  9. package/dist/agent-title-text.d.ts +3 -0
  10. package/dist/agent-title-text.js +7 -0
  11. package/dist/agent-title-text.js.map +1 -0
  12. package/dist/app/renderer/agentactivity.css +6773 -0
  13. package/dist/app/renderer/assets/icons/agent-sessions-filled.svg +1 -0
  14. package/dist/app/renderer/assets/icons/agents/claude-rounded.png +0 -0
  15. package/dist/app/renderer/assets/icons/agents/codex-rounded.png +0 -0
  16. package/dist/app/renderer/assets/icons/agents/gemini-rounded.png +0 -0
  17. package/dist/app/renderer/assets/icons/agents/hermes-rounded.png +0 -0
  18. package/dist/app/renderer/assets/icons/agents/manage-agent-claude-code.png +0 -0
  19. package/dist/app/renderer/assets/icons/agents/manage-agent-codex.png +0 -0
  20. package/dist/app/renderer/assets/icons/agents/manage-agent-gemini.png +0 -0
  21. package/dist/app/renderer/assets/icons/agents/manage-agent-hermes.png +0 -0
  22. package/dist/app/renderer/assets/icons/agents/manage-agent-nextop.png +0 -0
  23. package/dist/app/renderer/assets/icons/agents/manage-agent-openclaw.png +0 -0
  24. package/dist/app/renderer/assets/icons/agents/nextop-doc-rounded.png +0 -0
  25. package/dist/app/renderer/assets/icons/agents/openclaw-rounded.png +0 -0
  26. package/dist/app/renderer/assets/icons/agents/workspace-dock-agent-claude-code.png +0 -0
  27. package/dist/app/renderer/assets/icons/agents/workspace-dock-agent-codex.png +0 -0
  28. package/dist/app/renderer/assets/icons/agents/workspace-dock-agent-gemini.png +0 -0
  29. package/dist/app/renderer/assets/icons/agents/workspace-dock-agent-nexight.png +0 -0
  30. package/dist/app/renderer/assets/icons/agents/workspace-dock-agent-openclaw.png +0 -0
  31. package/dist/app/renderer/assets/icons/code-filled.svg +1 -0
  32. package/dist/app/renderer/assets/icons/doc-filled.svg +1 -0
  33. package/dist/app/renderer/assets/icons/folder-filled.svg +1 -0
  34. package/dist/app/renderer/assets/icons/image-filled.svg +1 -0
  35. package/dist/app/renderer/assets/icons/issue-filled.svg +1 -0
  36. package/dist/app/renderer/assets/icons/product-filled.svg +1 -0
  37. package/dist/app/renderer/assets/icons/user-avatar-placeholder.png +0 -0
  38. package/dist/app/renderer/assets/icons/video-filled.svg +1 -0
  39. package/dist/chunk-22L4VWUR.js +70 -0
  40. package/dist/chunk-22L4VWUR.js.map +1 -0
  41. package/dist/chunk-3D5VTIKP.js +93 -0
  42. package/dist/chunk-3D5VTIKP.js.map +1 -0
  43. package/dist/chunk-AF5CXBJN.js +3075 -0
  44. package/dist/chunk-AF5CXBJN.js.map +1 -0
  45. package/dist/chunk-BABBC24I.js +28 -0
  46. package/dist/chunk-BABBC24I.js.map +1 -0
  47. package/dist/chunk-GCBDIQDX.js +22 -0
  48. package/dist/chunk-GCBDIQDX.js.map +1 -0
  49. package/dist/chunk-HSR5DI6O.js +4695 -0
  50. package/dist/chunk-HSR5DI6O.js.map +1 -0
  51. package/dist/chunk-IVPB4MLI.js +7 -0
  52. package/dist/chunk-IVPB4MLI.js.map +1 -0
  53. package/dist/chunk-KCC3GNPB.js +13 -0
  54. package/dist/chunk-KCC3GNPB.js.map +1 -0
  55. package/dist/chunk-PJP5BUU6.js +18 -0
  56. package/dist/chunk-PJP5BUU6.js.map +1 -0
  57. package/dist/chunk-RORLLV27.js +144 -0
  58. package/dist/chunk-RORLLV27.js.map +1 -0
  59. package/dist/chunk-UJWUGMWC.js +128 -0
  60. package/dist/chunk-UJWUGMWC.js.map +1 -0
  61. package/dist/chunk-UKQIGNN3.js +921 -0
  62. package/dist/chunk-UKQIGNN3.js.map +1 -0
  63. package/dist/chunk-ZP7P7DYO.js +270 -0
  64. package/dist/chunk-ZP7P7DYO.js.map +1 -0
  65. package/dist/chunk-ZX5PDYAS.js +346 -0
  66. package/dist/chunk-ZX5PDYAS.js.map +1 -0
  67. package/dist/claude-rounded-F6VPQETB.png +0 -0
  68. package/dist/codex-rounded-SC63MZAW.png +0 -0
  69. package/dist/gemini-rounded-O4KAJFIM.png +0 -0
  70. package/dist/hermes-rounded-QGDHBNRJ.png +0 -0
  71. package/dist/i18n/index.d.ts +4580 -0
  72. package/dist/i18n/index.js +19 -0
  73. package/dist/i18n/index.js.map +1 -0
  74. package/dist/index.d.ts +775 -0
  75. package/dist/index.js +33699 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/manage-agent-claude-code-F6VPQETB.png +0 -0
  78. package/dist/manage-agent-codex-SC63MZAW.png +0 -0
  79. package/dist/manage-agent-gemini-O4KAJFIM.png +0 -0
  80. package/dist/manage-agent-hermes-QGDHBNRJ.png +0 -0
  81. package/dist/manage-agent-nextop-UFAQ22K2.png +0 -0
  82. package/dist/manage-agent-openclaw-24U7O6CA.png +0 -0
  83. package/dist/mention-file-presentation.d.ts +16 -0
  84. package/dist/mention-file-presentation.js +10 -0
  85. package/dist/mention-file-presentation.js.map +1 -0
  86. package/dist/nextop-doc-rounded-UFAQ22K2.png +0 -0
  87. package/dist/openclaw-rounded-24U7O6CA.png +0 -0
  88. package/dist/user-avatar-placeholder-WP2373TS.png +0 -0
  89. package/dist/workbench/contribution.d.ts +42 -0
  90. package/dist/workbench/contribution.js +24 -0
  91. package/dist/workbench/contribution.js.map +1 -0
  92. package/dist/workbench/index.d.ts +24 -0
  93. package/dist/workbench/index.js +82 -0
  94. package/dist/workbench/index.js.map +1 -0
  95. package/dist/workbench/launch.d.ts +49 -0
  96. package/dist/workbench/launch.js +23 -0
  97. package/dist/workbench/launch.js.map +1 -0
  98. package/dist/workbench/providerCatalog.d.ts +15 -0
  99. package/dist/workbench/providerCatalog.js +27 -0
  100. package/dist/workbench/providerCatalog.js.map +1 -0
  101. package/dist/workbench/state.d.ts +22 -0
  102. package/dist/workbench/state.js +22 -0
  103. package/dist/workbench/state.js.map +1 -0
  104. package/dist/workbench/types.d.ts +43 -0
  105. package/dist/workbench/types.js +7 -0
  106. package/dist/workbench/types.js.map +1 -0
  107. package/dist/workspace-agent-generated-files.d.ts +2 -0
  108. package/dist/workspace-agent-generated-files.js +21 -0
  109. package/dist/workspace-agent-generated-files.js.map +1 -0
  110. package/dist/workspaceAgentActivityListViewModel-PvLQDj60.d.ts +309 -0
  111. package/dist/workspaceLinkActions-Bwa-phu8.d.ts +51 -0
  112. package/package.json +166 -0
@@ -0,0 +1,3075 @@
1
+ import {
2
+ translate,
3
+ useTranslation
4
+ } from "./chunk-HSR5DI6O.js";
5
+ import {
6
+ resolveAgentWorkspaceFileVisualKind
7
+ } from "./chunk-PJP5BUU6.js";
8
+
9
+ // agentActivityRuntime.tsx
10
+ import {
11
+ createContext,
12
+ useContext,
13
+ useSyncExternalStore
14
+ } from "react";
15
+ import { jsx } from "react/jsx-runtime";
16
+ var AgentActivityRuntimeContext = createContext(
17
+ null
18
+ );
19
+ var currentAgentActivityRuntime = null;
20
+ function AgentActivityRuntimeProvider({
21
+ children,
22
+ runtime
23
+ }) {
24
+ currentAgentActivityRuntime = runtime ?? null;
25
+ return /* @__PURE__ */ jsx(AgentActivityRuntimeContext.Provider, { value: runtime ?? null, children });
26
+ }
27
+ function useAgentActivityRuntime() {
28
+ const runtime = useContext(AgentActivityRuntimeContext) ?? getTestAgentActivityRuntime();
29
+ if (!runtime) {
30
+ throw new Error(
31
+ "AgentActivityRuntimeProvider is missing an AgentActivityRuntime instance."
32
+ );
33
+ }
34
+ return runtime;
35
+ }
36
+ function useOptionalAgentActivityRuntime() {
37
+ return useContext(AgentActivityRuntimeContext) ?? getTestAgentActivityRuntime();
38
+ }
39
+ function useAgentActivitySnapshot(workspaceId) {
40
+ const runtime = useAgentActivityRuntime();
41
+ const normalizedWorkspaceId = workspaceId.trim();
42
+ return useSyncExternalStore(
43
+ (listener) => runtime.subscribe(normalizedWorkspaceId, listener),
44
+ () => runtime.getSnapshot(normalizedWorkspaceId),
45
+ () => runtime.getSnapshot(normalizedWorkspaceId)
46
+ );
47
+ }
48
+ function getAgentActivityRuntime() {
49
+ const runtime = getExplicitWindowTestAgentActivityRuntime() ?? currentAgentActivityRuntime ?? getTestAgentActivityRuntime();
50
+ if (!runtime) {
51
+ throw new Error(
52
+ "AgentActivityRuntimeProvider is missing an AgentActivityRuntime instance."
53
+ );
54
+ }
55
+ return runtime;
56
+ }
57
+ function getOptionalAgentActivityRuntime() {
58
+ return getExplicitWindowTestAgentActivityRuntime() ?? currentAgentActivityRuntime ?? getTestAgentActivityRuntime();
59
+ }
60
+ function resetAgentActivityRuntimeForTests() {
61
+ if (process.env.NODE_ENV === "test") {
62
+ currentAgentActivityRuntime = null;
63
+ }
64
+ }
65
+ function setAgentActivityRuntimeForTests(runtime) {
66
+ if (process.env.NODE_ENV === "test") {
67
+ currentAgentActivityRuntime = runtime;
68
+ }
69
+ }
70
+ function getTestAgentActivityRuntime() {
71
+ if (process.env.NODE_ENV !== "test") {
72
+ return null;
73
+ }
74
+ if (typeof window === "undefined") {
75
+ return null;
76
+ }
77
+ const explicitRuntime = getExplicitWindowTestAgentActivityRuntime();
78
+ if (explicitRuntime) {
79
+ return explicitRuntime;
80
+ }
81
+ if (currentAgentActivityRuntime) {
82
+ return currentAgentActivityRuntime;
83
+ }
84
+ const testRuntime = window.agentActivityRuntime;
85
+ return testRuntime ?? null;
86
+ }
87
+ function getExplicitWindowTestAgentActivityRuntime() {
88
+ if (process.env.NODE_ENV !== "test" || typeof window === "undefined") {
89
+ return null;
90
+ }
91
+ const testDescriptor = Object.getOwnPropertyDescriptor(
92
+ window,
93
+ "agentActivityRuntime"
94
+ );
95
+ if (!testDescriptor || !("value" in testDescriptor)) {
96
+ return null;
97
+ }
98
+ return testDescriptor.value ?? null;
99
+ }
100
+
101
+ // agentActivityHost.tsx
102
+ import {
103
+ createContext as createContext2,
104
+ useContext as useContext2
105
+ } from "react";
106
+
107
+ // host/agentHostApi.ts
108
+ function toAgentHostRuntimeApi(hostApi) {
109
+ return {
110
+ account: hostApi.account,
111
+ agentGuiBatch: hostApi.agentGuiBatch ?? {},
112
+ clipboard: hostApi.clipboard,
113
+ debug: hostApi.debug,
114
+ filesystem: hostApi.filesystem,
115
+ meta: hostApi.meta,
116
+ onHostEvent: hostApi.onHostEvent,
117
+ runtime: hostApi.runtime,
118
+ userProjects: hostApi.userProjects,
119
+ workspace: hostApi.workspace,
120
+ workspaceAgentProbes: hostApi.workspaceAgentProbes
121
+ };
122
+ }
123
+
124
+ // agentActivityHost.tsx
125
+ import { jsx as jsx2 } from "react/jsx-runtime";
126
+ var AgentActivityHostContext = createContext2(
127
+ null
128
+ );
129
+ var currentAgentHostApi = null;
130
+ function AgentActivityHostProvider({
131
+ agentActivityRuntime,
132
+ agentHostApi,
133
+ children
134
+ }) {
135
+ const resolvedAgentHostApi = agentHostApi ? toAgentHostRuntimeApi(agentHostApi) : null;
136
+ currentAgentHostApi = resolvedAgentHostApi;
137
+ return /* @__PURE__ */ jsx2(AgentActivityRuntimeProvider, { runtime: agentActivityRuntime, children: /* @__PURE__ */ jsx2(AgentActivityHostContext.Provider, { value: resolvedAgentHostApi, children }) });
138
+ }
139
+ function useAgentHostApi() {
140
+ const agentHostApi = useContext2(AgentActivityHostContext) ?? getTestAgentHostApi();
141
+ if (!agentHostApi) {
142
+ throw new Error(
143
+ "AgentActivityHostProvider is missing an agentHostApi instance."
144
+ );
145
+ }
146
+ return agentHostApi;
147
+ }
148
+ function useOptionalAgentHostApi() {
149
+ return useContext2(AgentActivityHostContext) ?? getTestAgentHostApi();
150
+ }
151
+ function getOptionalAgentHostApi() {
152
+ return getExplicitWindowTestAgentHostApi() ?? currentAgentHostApi ?? getTestAgentHostApi();
153
+ }
154
+ function getTestAgentHostApi() {
155
+ if (process.env.NODE_ENV !== "test") {
156
+ return null;
157
+ }
158
+ if (typeof window === "undefined") {
159
+ return null;
160
+ }
161
+ const explicitAgentHostApi = getExplicitWindowTestAgentHostApi();
162
+ if (explicitAgentHostApi) {
163
+ return explicitAgentHostApi;
164
+ }
165
+ if (currentAgentHostApi) {
166
+ return currentAgentHostApi;
167
+ }
168
+ const testAgentHostApi = window.agentHostApi;
169
+ return testAgentHostApi ? toAgentHostRuntimeApi(testAgentHostApi) : null;
170
+ }
171
+ function getExplicitWindowTestAgentHostApi() {
172
+ if (process.env.NODE_ENV !== "test" || typeof window === "undefined") {
173
+ return null;
174
+ }
175
+ const testDescriptor = Object.getOwnPropertyDescriptor(
176
+ window,
177
+ "agentHostApi"
178
+ );
179
+ if (!testDescriptor || !("value" in testDescriptor)) {
180
+ return null;
181
+ }
182
+ const testAgentHostApi = testDescriptor.value;
183
+ return testAgentHostApi ? toAgentHostRuntimeApi(testAgentHostApi) : null;
184
+ }
185
+
186
+ // app/renderer/assets/icons/agents/manage-agent-claude-code.png
187
+ var manage_agent_claude_code_default = "./manage-agent-claude-code-F6VPQETB.png";
188
+
189
+ // app/renderer/assets/icons/agents/manage-agent-codex.png
190
+ var manage_agent_codex_default = "./manage-agent-codex-SC63MZAW.png";
191
+
192
+ // app/renderer/assets/icons/agents/manage-agent-gemini.png
193
+ var manage_agent_gemini_default = "./manage-agent-gemini-O4KAJFIM.png";
194
+
195
+ // app/renderer/assets/icons/agents/manage-agent-hermes.png
196
+ var manage_agent_hermes_default = "./manage-agent-hermes-QGDHBNRJ.png";
197
+
198
+ // app/renderer/assets/icons/agents/manage-agent-nextop.png
199
+ var manage_agent_nextop_default = "./manage-agent-nextop-UFAQ22K2.png";
200
+
201
+ // app/renderer/assets/icons/agents/manage-agent-openclaw.png
202
+ var manage_agent_openclaw_default = "./manage-agent-openclaw-24U7O6CA.png";
203
+
204
+ // app/renderer/assets/icons/agents/claude-rounded.png
205
+ var claude_rounded_default = "./claude-rounded-F6VPQETB.png";
206
+
207
+ // app/renderer/assets/icons/agents/codex-rounded.png
208
+ var codex_rounded_default = "./codex-rounded-SC63MZAW.png";
209
+
210
+ // app/renderer/assets/icons/agents/gemini-rounded.png
211
+ var gemini_rounded_default = "./gemini-rounded-O4KAJFIM.png";
212
+
213
+ // app/renderer/assets/icons/agents/hermes-rounded.png
214
+ var hermes_rounded_default = "./hermes-rounded-QGDHBNRJ.png";
215
+
216
+ // app/renderer/assets/icons/agents/nextop-doc-rounded.png
217
+ var nextop_doc_rounded_default = "./nextop-doc-rounded-UFAQ22K2.png";
218
+
219
+ // app/renderer/assets/icons/agents/openclaw-rounded.png
220
+ var openclaw_rounded_default = "./openclaw-rounded-24U7O6CA.png";
221
+
222
+ // shared/managedAgentProviders.ts
223
+ function normalizeManagedAgentProvider(provider) {
224
+ const normalized = provider?.trim().toLowerCase().replace(/[_\s]+/gu, "-") ?? "";
225
+ switch (normalized) {
226
+ case "claude":
227
+ case "claude-code":
228
+ return "claude-code";
229
+ case "nexight":
230
+ case "nextop-doc":
231
+ return "nextop";
232
+ default:
233
+ return normalized;
234
+ }
235
+ }
236
+
237
+ // shared/managedAgentIcons.ts
238
+ var MANAGED_AGENT_ICON_URLS = {
239
+ "claude-code": manage_agent_claude_code_default,
240
+ codex: manage_agent_codex_default,
241
+ gemini: manage_agent_gemini_default,
242
+ hermes: manage_agent_hermes_default,
243
+ nextop: manage_agent_nextop_default,
244
+ openclaw: manage_agent_openclaw_default
245
+ };
246
+ var MANAGED_AGENT_ICON_ROUNDED_URLS = {
247
+ "claude-code": claude_rounded_default,
248
+ codex: codex_rounded_default,
249
+ gemini: gemini_rounded_default,
250
+ hermes: hermes_rounded_default,
251
+ nextop: nextop_doc_rounded_default,
252
+ openclaw: openclaw_rounded_default
253
+ };
254
+ var MANAGED_AGENT_ROUNDED_ICON_FALLBACK_URL = nextop_doc_rounded_default;
255
+ var MANAGED_AGENT_ICON_FALLBACK_URL = manage_agent_nextop_default;
256
+ function managedAgentRoundedIconUrl(provider) {
257
+ return MANAGED_AGENT_ICON_ROUNDED_URLS[normalizeManagedAgentProvider(provider)] ?? MANAGED_AGENT_ROUNDED_ICON_FALLBACK_URL;
258
+ }
259
+
260
+ // shared/agentConversation/approvalOptionPresentation.ts
261
+ function approvalOptionDisplayLabel(option, intent = {}) {
262
+ const idToken = normalizeApprovalOptionToken(option.id);
263
+ const kindToken = normalizeApprovalOptionToken(option.kind);
264
+ const label = option.label.trim();
265
+ const specificTranslationKey = approvalOptionSpecificTranslationKey(
266
+ idToken,
267
+ label
268
+ );
269
+ if (specificTranslationKey) {
270
+ return translate(specificTranslationKey);
271
+ }
272
+ const providerLabelTranslation = approvalOptionProviderLabelTranslation(label);
273
+ if (providerLabelTranslation) {
274
+ return translate(
275
+ providerLabelTranslation.key,
276
+ providerLabelTranslation.params
277
+ );
278
+ }
279
+ if ((idToken === "allowonce" || kindToken === "allowonce") && isGenericAllowOnceLabel(label)) {
280
+ return translate("agentHost.agentGui.approvalOptions.allowOnce");
281
+ }
282
+ if ((idToken === "allowalways" || idToken === "allowall") && isGenericApprovalLabel(label)) {
283
+ return translate("agentHost.agentGui.approvalOptions.allowAlways");
284
+ }
285
+ if (idToken === "rejectalways" || kindToken === "rejectalways") {
286
+ return translate("agentHost.agentGui.approvalOptions.rejectAlways");
287
+ }
288
+ if (idToken === "rejectonce" || idToken === "reject" || idToken === "deny" || kindToken === "rejectonce" || kindToken === "reject" || kindToken === "deny") {
289
+ return intent.feedback ? translate("agentHost.agentGui.approvalOptions.rejectWithFollowUp") : translate("agentHost.agentGui.approvalOptions.rejectOnce");
290
+ }
291
+ return label || option.id;
292
+ }
293
+ function approvalOptionVisualPresentation(option, intent = {}) {
294
+ const commandPrefix = approvalOptionCommandPrefix(option.label);
295
+ if (commandPrefix) {
296
+ return {
297
+ label: translate(
298
+ "agentHost.agentGui.approvalOptions.allowAlwaysForCommandPrefixLead"
299
+ ),
300
+ commandPrefix
301
+ };
302
+ }
303
+ return {
304
+ label: approvalOptionDisplayLabel(option, intent)
305
+ };
306
+ }
307
+ function normalizeApprovalOptionToken(value) {
308
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
309
+ }
310
+ function approvalOptionSpecificTranslationKey(token, label) {
311
+ const labelToken = normalizeApprovalOptionToken(label);
312
+ switch (token) {
313
+ case "bypasspermissions":
314
+ return "agentHost.agentGui.approvalOptions.bypassPermissions";
315
+ case "auto":
316
+ return labelToken === "yesanduseautomode" ? "agentHost.agentGui.approvalOptions.autoMode" : null;
317
+ case "acceptedits":
318
+ return "agentHost.agentGui.approvalOptions.acceptEdits";
319
+ case "default":
320
+ return labelToken === "yesandmanuallyapproveedits" ? "agentHost.agentGui.approvalOptions.manualApproval" : null;
321
+ default:
322
+ return null;
323
+ }
324
+ }
325
+ function approvalOptionProviderLabelTranslation(label) {
326
+ const commandPrefix = approvalOptionCommandPrefix(label);
327
+ if (commandPrefix) {
328
+ return {
329
+ key: "agentHost.agentGui.approvalOptions.allowAlwaysForCommandPrefix",
330
+ params: { command: commandPrefix }
331
+ };
332
+ }
333
+ const allowScopeMatch = label.match(/^Yes,\s*and don't ask again for (.+)$/i);
334
+ if (allowScopeMatch?.[1]) {
335
+ return {
336
+ key: "agentHost.agentGui.approvalOptions.allowAlwaysForScope",
337
+ params: { scope: allowScopeMatch[1] }
338
+ };
339
+ }
340
+ const alwaysAllowMatch = label.match(/^Always Allow\s+(.+)$/i);
341
+ if (alwaysAllowMatch?.[1]) {
342
+ return {
343
+ key: "agentHost.agentGui.approvalOptions.alwaysAllowScope",
344
+ params: { scope: alwaysAllowMatch[1] }
345
+ };
346
+ }
347
+ return null;
348
+ }
349
+ function approvalOptionCommandPrefix(label) {
350
+ const commandPrefixMatch = label.match(
351
+ /^Yes,\s*and don't ask again for commands that start with `([^`]+)`$/i
352
+ );
353
+ return commandPrefixMatch?.[1]?.trim() || null;
354
+ }
355
+ function isGenericApprovalLabel(label) {
356
+ const token = normalizeApprovalOptionToken(label);
357
+ return token === "" || token === "allowalways" || token === "allowall" || token === "alwaysallow" || token === "yesanddontaskagain";
358
+ }
359
+ function isGenericAllowOnceLabel(label) {
360
+ const token = normalizeApprovalOptionToken(label);
361
+ return token === "" || token === "allow" || token === "allowonce" || token === "yes" || token === "yesproceed" || token === "yesandproceed";
362
+ }
363
+
364
+ // shared/agentConversation/promptToolDetails.ts
365
+ function getPromptToolDetails(input) {
366
+ if (!input) {
367
+ return [];
368
+ }
369
+ const detailInput = resolveToolDetailInput(input);
370
+ const command = commandStringValue(detailInput.command) ?? commandStringValue(detailInput.cmd);
371
+ if (command) {
372
+ return [
373
+ {
374
+ kind: "command",
375
+ value: command,
376
+ ...stringValue(detailInput.description) ? { meta: stringValue(detailInput.description) } : {}
377
+ }
378
+ ];
379
+ }
380
+ const filePath = stringValue(detailInput.file_path) ?? stringValue(detailInput.filePath) ?? stringValue(detailInput.path) ?? stringValue(detailInput.notebook_path);
381
+ if (filePath) {
382
+ const lineRange = formatLineRange(detailInput);
383
+ return [
384
+ {
385
+ kind: "path",
386
+ value: filePath,
387
+ ...lineRange ? { meta: lineRange } : {}
388
+ }
389
+ ];
390
+ }
391
+ const query = stringValue(detailInput.query) ?? stringValue(detailInput.search_query) ?? stringValue(detailInput.searchQuery) ?? stringValue(detailInput.pattern);
392
+ if (query) {
393
+ return [
394
+ {
395
+ kind: "query",
396
+ value: query
397
+ }
398
+ ];
399
+ }
400
+ return [];
401
+ }
402
+ function isPromptRequestIdTitle(value) {
403
+ return /^request(?:id|ID)\s*:/u.test(value.trim());
404
+ }
405
+ function resolveToolDetailInput(input) {
406
+ const toolCall = objectValue(input.toolCall);
407
+ return firstObjectValue(input, [
408
+ "command",
409
+ "cmd",
410
+ "file_path",
411
+ "filePath",
412
+ "path",
413
+ "notebook_path",
414
+ "query",
415
+ "search_query",
416
+ "searchQuery",
417
+ "pattern"
418
+ ]) ?? firstObjectValue(toolCall, [
419
+ "input",
420
+ "rawInput",
421
+ "raw_input",
422
+ "arguments",
423
+ "args"
424
+ ]) ?? toolCall ?? input;
425
+ }
426
+ function firstObjectValue(input, keys) {
427
+ if (!input) {
428
+ return null;
429
+ }
430
+ if (keys.some((key) => stringValue(input[key]))) {
431
+ return input;
432
+ }
433
+ for (const key of keys) {
434
+ const value = objectValue(input[key]);
435
+ if (value) {
436
+ return value;
437
+ }
438
+ }
439
+ return null;
440
+ }
441
+ function formatLineRange(input) {
442
+ const start = numericValue(input.startLine) ?? numericValue(input.start_line);
443
+ const end = numericValue(input.endLine) ?? numericValue(input.end_line);
444
+ if (start === null || end === null) {
445
+ return null;
446
+ }
447
+ return start === end ? `L${start}` : `L${start}-${end}`;
448
+ }
449
+ function stringValue(value) {
450
+ return typeof value === "string" && value.trim() ? value.trim() : null;
451
+ }
452
+ function commandStringValue(value) {
453
+ if (typeof value === "string") {
454
+ return stringValue(value);
455
+ }
456
+ if (!Array.isArray(value)) {
457
+ return null;
458
+ }
459
+ const shellFlag = stringValue(value[value.length - 2]);
460
+ const shellCommand = stringValue(value[value.length - 1]);
461
+ if ((shellFlag === "-c" || shellFlag === "-lc") && shellCommand) {
462
+ return shellCommand;
463
+ }
464
+ const parts = value.flatMap((part) => {
465
+ const text = stringValue(part);
466
+ return text ? [text] : [];
467
+ });
468
+ return parts.length > 0 ? parts.join(" ") : null;
469
+ }
470
+ function objectValue(value) {
471
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
472
+ }
473
+ function numericValue(value) {
474
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
475
+ }
476
+
477
+ // shared/agentConversation/components/AgentInteractivePromptSurface.tsx
478
+ import {
479
+ useCallback as useCallback2,
480
+ useEffect as useEffect2,
481
+ useMemo,
482
+ useRef as useRef2,
483
+ useState
484
+ } from "react";
485
+
486
+ // app/renderer/components/icons/MessageSquareMoreIcon.tsx
487
+ import {
488
+ forwardRef,
489
+ useCallback,
490
+ useEffect,
491
+ useImperativeHandle,
492
+ useRef
493
+ } from "react";
494
+ import {
495
+ motion,
496
+ useAnimation,
497
+ useReducedMotion
498
+ } from "framer-motion";
499
+
500
+ // app/renderer/lib/utils.ts
501
+ import { clsx } from "clsx";
502
+ import { twMerge } from "tailwind-merge";
503
+ function cn(...inputs) {
504
+ return twMerge(clsx(inputs));
505
+ }
506
+
507
+ // app/renderer/components/icons/MessageSquareMoreIcon.tsx
508
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
509
+ var DOT_TRANSITION = {
510
+ times: [0, 0.1, 0.1, 0.2, 0.5, 0.6, 0.6, 0.7],
511
+ duration: 1.5
512
+ };
513
+ var DOT_VARIANTS = {
514
+ normal: {
515
+ opacity: 1
516
+ },
517
+ animate: (custom) => ({
518
+ opacity: [1, 0, 0, 1, 1, 0, 0, 1],
519
+ transition: {
520
+ opacity: {
521
+ ...DOT_TRANSITION,
522
+ times: DOT_TRANSITION.times.map(
523
+ (time, index) => index === 2 || index === 3 || index === 6 || index === 7 ? time + custom * 0.1 : time
524
+ )
525
+ }
526
+ }
527
+ }),
528
+ active: (custom) => ({
529
+ opacity: [1, 0, 0, 1, 1, 0, 0, 1],
530
+ transition: {
531
+ opacity: {
532
+ ...DOT_TRANSITION,
533
+ repeat: Infinity,
534
+ times: DOT_TRANSITION.times.map(
535
+ (time, index) => index === 2 || index === 3 || index === 6 || index === 7 ? time + custom * 0.1 : time
536
+ )
537
+ }
538
+ }
539
+ })
540
+ };
541
+ var MessageSquareMoreIcon = forwardRef(
542
+ ({
543
+ active = false,
544
+ onMouseEnter,
545
+ onMouseLeave,
546
+ className,
547
+ size = 28,
548
+ ...props
549
+ }, ref) => {
550
+ const controls = useAnimation();
551
+ const reduceMotion = useReducedMotion();
552
+ const isControlledRef = useRef(false);
553
+ const startAnimation = useCallback(() => {
554
+ if (reduceMotion) {
555
+ return;
556
+ }
557
+ void controls.start(active ? "active" : "animate");
558
+ }, [active, controls, reduceMotion]);
559
+ const stopAnimation = useCallback(() => {
560
+ void controls.start("normal");
561
+ }, [controls]);
562
+ useImperativeHandle(ref, () => {
563
+ isControlledRef.current = true;
564
+ return {
565
+ startAnimation,
566
+ stopAnimation
567
+ };
568
+ });
569
+ useEffect(() => {
570
+ if (active) {
571
+ startAnimation();
572
+ return;
573
+ }
574
+ stopAnimation();
575
+ }, [active, startAnimation, stopAnimation]);
576
+ const handleMouseEnter = useCallback(
577
+ (event) => {
578
+ if (isControlledRef.current) {
579
+ onMouseEnter?.(event);
580
+ } else {
581
+ startAnimation();
582
+ }
583
+ },
584
+ [onMouseEnter, startAnimation]
585
+ );
586
+ const handleMouseLeave = useCallback(
587
+ (event) => {
588
+ if (isControlledRef.current) {
589
+ onMouseLeave?.(event);
590
+ } else {
591
+ stopAnimation();
592
+ }
593
+ },
594
+ [onMouseLeave, stopAnimation]
595
+ );
596
+ return /* @__PURE__ */ jsx3(
597
+ "div",
598
+ {
599
+ className: cn("inline-flex items-center justify-center", className),
600
+ onMouseEnter: handleMouseEnter,
601
+ onMouseLeave: handleMouseLeave,
602
+ ...props,
603
+ children: /* @__PURE__ */ jsxs(
604
+ "svg",
605
+ {
606
+ fill: "none",
607
+ height: size,
608
+ stroke: "currentColor",
609
+ strokeLinecap: "round",
610
+ strokeLinejoin: "round",
611
+ strokeWidth: "2",
612
+ viewBox: "0 0 24 24",
613
+ width: size,
614
+ xmlns: "http://www.w3.org/2000/svg",
615
+ children: [
616
+ /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
617
+ /* @__PURE__ */ jsx3(
618
+ motion.path,
619
+ {
620
+ animate: controls,
621
+ custom: 0,
622
+ d: "M8 10h.01",
623
+ variants: DOT_VARIANTS
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsx3(
627
+ motion.path,
628
+ {
629
+ animate: controls,
630
+ custom: 1,
631
+ d: "M12 10h.01",
632
+ variants: DOT_VARIANTS
633
+ }
634
+ ),
635
+ /* @__PURE__ */ jsx3(
636
+ motion.path,
637
+ {
638
+ animate: controls,
639
+ custom: 2,
640
+ d: "M16 10h.01",
641
+ variants: DOT_VARIANTS
642
+ }
643
+ )
644
+ ]
645
+ }
646
+ )
647
+ }
648
+ );
649
+ }
650
+ );
651
+ MessageSquareMoreIcon.displayName = "MessageSquareMoreIcon";
652
+
653
+ // app/renderer/components/ui/spinner.tsx
654
+ import { Spinner } from "@tutti-os/ui-system";
655
+
656
+ // shared/agentConversation/components/AgentInteractivePromptSurface.tsx
657
+ import {
658
+ ShortcutBadge,
659
+ Tooltip,
660
+ TooltipContent,
661
+ TooltipProvider,
662
+ TooltipTrigger
663
+ } from "@tutti-os/ui-system";
664
+
665
+ // agent-gui/agentGuiNode/AgentGUIConversation.styles.ts
666
+ var styles = {
667
+ interactivePrompt: "agent-gui-conversation__interactive-prompt",
668
+ interactivePromptCard: "agent-gui-conversation__interactive-prompt-card",
669
+ interactivePromptHeader: "agent-gui-conversation__interactive-prompt-header",
670
+ interactivePromptLead: "agent-gui-conversation__interactive-prompt-lead",
671
+ interactivePromptMeta: "agent-gui-conversation__interactive-prompt-meta",
672
+ interactivePromptQuestion: "agent-gui-conversation__interactive-prompt-question",
673
+ interactivePromptOptions: "agent-gui-conversation__interactive-prompt-options",
674
+ interactiveOptionDisplay: "agent-gui-conversation__interactive-option-display",
675
+ interactiveOptionButton: "agent-gui-conversation__interactive-option-button",
676
+ interactiveOptionTitle: "agent-gui-conversation__interactive-option-title",
677
+ interactiveOptionDescription: "agent-gui-conversation__interactive-option-description",
678
+ interactiveOptionCommandDescription: "agent-gui-conversation__interactive-option-command-description",
679
+ interactiveOptionCommandTooltip: "agent-gui-conversation__interactive-option-command-tooltip",
680
+ interactiveOptionShortcut: "agent-gui-conversation__interactive-option-shortcut",
681
+ interactiveOptionSpinner: "agent-gui-conversation__interactive-option-spinner",
682
+ interactiveFeedbackComposer: "agent-gui-conversation__interactive-feedback-composer",
683
+ interactivePromptTextarea: "agent-gui-conversation__interactive-prompt-textarea",
684
+ interactiveFeedbackSendButton: "agent-gui-conversation__interactive-feedback-send-button",
685
+ interactivePromptFooter: "agent-gui-conversation__interactive-prompt-footer",
686
+ interactivePromptActions: "agent-gui-conversation__interactive-prompt-actions",
687
+ userMessageFlow: "agent-gui-conversation__user-message-flow",
688
+ assistantMessageFlow: "agent-gui-conversation__assistant-message-flow",
689
+ messageGroup: "agent-gui-conversation__message-group",
690
+ userMessageBubble: "agent-gui-conversation__user-message-bubble",
691
+ assistantMarkdown: "agent-gui-conversation__assistant-markdown"
692
+ };
693
+ var AgentGUIConversation_styles_default = styles;
694
+
695
+ // shared/agentConversation/components/AgentInteractivePromptSurface.tsx
696
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
697
+ var COMMAND_TOOLTIP_DELAY_MS = 1e3;
698
+ function AgentInteractivePromptSurface({
699
+ prompt,
700
+ edgeGlow = false,
701
+ embedded = false,
702
+ keyboardShortcuts = true,
703
+ isSubmitting,
704
+ onSubmit,
705
+ labels
706
+ }) {
707
+ "use memo";
708
+ if (prompt.kind === "approval") {
709
+ return /* @__PURE__ */ jsx4(
710
+ ApprovalPromptSurface,
711
+ {
712
+ prompt,
713
+ embedded,
714
+ edgeGlow,
715
+ keyboardShortcuts,
716
+ isSubmitting,
717
+ onSubmit,
718
+ labels
719
+ }
720
+ );
721
+ }
722
+ if (prompt.kind === "exit-plan") {
723
+ return /* @__PURE__ */ jsx4(
724
+ ExitPlanPromptSurface,
725
+ {
726
+ prompt,
727
+ embedded,
728
+ edgeGlow,
729
+ isSubmitting,
730
+ onSubmit,
731
+ labels
732
+ }
733
+ );
734
+ }
735
+ return /* @__PURE__ */ jsx4(
736
+ AskUserPromptSurface,
737
+ {
738
+ prompt,
739
+ embedded,
740
+ edgeGlow,
741
+ isSubmitting,
742
+ onSubmit,
743
+ labels
744
+ }
745
+ );
746
+ }
747
+ function ApprovalPromptSurface({
748
+ prompt,
749
+ embedded = false,
750
+ edgeGlow = false,
751
+ keyboardShortcuts = true,
752
+ isSubmitting,
753
+ onSubmit,
754
+ labels
755
+ }) {
756
+ "use memo";
757
+ const promptDetails = useMemo(
758
+ () => formatToolDetails(prompt.input ?? null),
759
+ [prompt.input]
760
+ );
761
+ const visiblePromptTitle = approvalPromptTitle(
762
+ prompt.title,
763
+ promptDetails.length
764
+ );
765
+ const details = useMemo(
766
+ () => filterDuplicatePromptTitleDetails(promptDetails, visiblePromptTitle),
767
+ [promptDetails, visiblePromptTitle]
768
+ );
769
+ const [submittingOptionId, setSubmittingOptionId] = useState(
770
+ null
771
+ );
772
+ const [pendingFeedbackOptionId, setPendingFeedbackOptionId] = useState(null);
773
+ const [feedback, setFeedback] = useState("");
774
+ const feedbackTextareaRef = useRef2(null);
775
+ const agentHostApi = useOptionalAgentHostApi() ?? getOptionalAgentHostApi();
776
+ const isDarwin = isDarwinPlatform(agentHostApi?.meta?.platform);
777
+ const feedbackOptionId = useMemo(
778
+ () => approvalFeedbackOptionId(prompt.options),
779
+ [prompt.options]
780
+ );
781
+ const feedbackValue = feedback.trim();
782
+ useEffect2(() => {
783
+ setSubmittingOptionId(null);
784
+ setPendingFeedbackOptionId(null);
785
+ setFeedback("");
786
+ }, [prompt.requestId]);
787
+ useEffect2(() => {
788
+ if (pendingFeedbackOptionId !== null) {
789
+ feedbackTextareaRef.current?.focus();
790
+ }
791
+ }, [pendingFeedbackOptionId]);
792
+ const submitOption = useCallback2(
793
+ (optionId) => {
794
+ const feedbackOption = feedbackOptionId === optionId;
795
+ if (feedbackOption && pendingFeedbackOptionId !== optionId) {
796
+ setFeedback("");
797
+ setPendingFeedbackOptionId(optionId);
798
+ return;
799
+ }
800
+ setSubmittingOptionId(optionId);
801
+ onSubmit({
802
+ requestId: prompt.requestId,
803
+ ...feedbackOption ? { action: "deny" } : {},
804
+ optionId,
805
+ ...feedbackOption && feedbackValue ? { payload: { denyMessage: feedbackValue } } : {}
806
+ });
807
+ },
808
+ [
809
+ feedbackOptionId,
810
+ feedbackValue,
811
+ onSubmit,
812
+ pendingFeedbackOptionId,
813
+ prompt.requestId
814
+ ]
815
+ );
816
+ useEffect2(() => {
817
+ if (!keyboardShortcuts || isSubmitting || submittingOptionId !== null || prompt.options.length === 0) {
818
+ return void 0;
819
+ }
820
+ const handleKeyDown = (event) => {
821
+ if (!isEnterLikeKey(event) || isEditableKeyboardTarget(event.target) || event.isComposing || event.altKey || event.shiftKey) {
822
+ return;
823
+ }
824
+ const optionIndex = event.metaKey || event.ctrlKey ? 1 : 0;
825
+ const option = prompt.options[optionIndex];
826
+ if (!option) {
827
+ return;
828
+ }
829
+ event.preventDefault();
830
+ event.stopPropagation();
831
+ submitOption(option.id);
832
+ };
833
+ window.addEventListener("keydown", handleKeyDown, true);
834
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
835
+ }, [
836
+ isSubmitting,
837
+ keyboardShortcuts,
838
+ prompt.options,
839
+ submitOption,
840
+ submittingOptionId
841
+ ]);
842
+ return /* @__PURE__ */ jsx4("section", { className: interactivePromptClassName(embedded), children: /* @__PURE__ */ jsxs2("div", { className: interactivePromptCardClassName(edgeGlow), children: [
843
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptLead, children: stripPromptTitlePunctuation(labels.approvalLead) }),
844
+ visiblePromptTitle ? /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptQuestion, children: visiblePromptTitle }) : null,
845
+ details.length > 0 ? /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptOptions, children: details.map((detail) => /* @__PURE__ */ jsxs2(
846
+ "div",
847
+ {
848
+ className: AgentGUIConversation_styles_default.interactiveOptionDisplay,
849
+ children: [
850
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionTitle, children: detail.label }),
851
+ /* @__PURE__ */ jsx4(PromptDetailValue, { detail }),
852
+ detail.meta ? /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionDescription, children: detail.meta }) : null
853
+ ]
854
+ },
855
+ `${detail.label}:${detail.value}`
856
+ )) }) : null,
857
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptOptions, children: prompt.options.map((option, optionIndex) => {
858
+ const showSpinner = submittingOptionId === option.id;
859
+ const optionSupportsFeedback = feedbackOptionId === option.id;
860
+ const optionLabel = approvalOptionDisplayLabel(option, {
861
+ feedback: optionSupportsFeedback
862
+ });
863
+ const optionPresentation = approvalOptionVisualPresentation(
864
+ option,
865
+ { feedback: optionSupportsFeedback }
866
+ );
867
+ const shortcutLabel = approvalOptionShortcutLabel(
868
+ optionIndex,
869
+ isDarwin
870
+ );
871
+ const showFeedbackComposer = pendingFeedbackOptionId === option.id;
872
+ if (showFeedbackComposer) {
873
+ return /* @__PURE__ */ jsxs2(
874
+ "div",
875
+ {
876
+ className: AgentGUIConversation_styles_default.interactiveFeedbackComposer,
877
+ children: [
878
+ /* @__PURE__ */ jsx4(
879
+ "textarea",
880
+ {
881
+ ref: feedbackTextareaRef,
882
+ value: feedback,
883
+ placeholder: labels.feedbackPlaceholder,
884
+ disabled: isSubmitting || submittingOptionId !== null,
885
+ className: AgentGUIConversation_styles_default.interactivePromptTextarea,
886
+ "aria-label": interactiveOptionLabel(
887
+ optionLabel,
888
+ option.description
889
+ ),
890
+ onChange: (event) => setFeedback(event.currentTarget.value),
891
+ onKeyDown: (event) => {
892
+ if (!keyboardShortcuts || event.key !== "Enter" || !event.metaKey && !event.ctrlKey || event.nativeEvent.isComposing) {
893
+ return;
894
+ }
895
+ event.preventDefault();
896
+ if (!feedbackValue) {
897
+ feedbackTextareaRef.current?.focus();
898
+ return;
899
+ }
900
+ submitOption(option.id);
901
+ }
902
+ }
903
+ ),
904
+ /* @__PURE__ */ jsx4(
905
+ "button",
906
+ {
907
+ type: "button",
908
+ className: AgentGUIConversation_styles_default.interactiveFeedbackSendButton,
909
+ disabled: isSubmitting || submittingOptionId !== null || !feedbackValue,
910
+ "aria-label": labels.sendFeedback,
911
+ title: labels.sendFeedback,
912
+ "aria-busy": showSpinner,
913
+ onClick: () => {
914
+ if (!feedbackValue) {
915
+ feedbackTextareaRef.current?.focus();
916
+ return;
917
+ }
918
+ submitOption(option.id);
919
+ },
920
+ children: showSpinner ? /* @__PURE__ */ jsx4(InteractiveOptionSpinner, {}) : /* @__PURE__ */ jsx4(SendFilledIcon, {})
921
+ }
922
+ )
923
+ ]
924
+ },
925
+ option.id
926
+ );
927
+ }
928
+ return /* @__PURE__ */ jsxs2(
929
+ "button",
930
+ {
931
+ type: "button",
932
+ className: AgentGUIConversation_styles_default.interactiveOptionButton,
933
+ "aria-label": interactiveOptionLabel(
934
+ optionLabel,
935
+ option.description
936
+ ),
937
+ disabled: isSubmitting || submittingOptionId !== null,
938
+ onClick: () => submitOption(option.id),
939
+ children: [
940
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionTitle, children: optionPresentation.label }),
941
+ optionPresentation.commandPrefix ? /* @__PURE__ */ jsx4(
942
+ CommandTextWithTooltip,
943
+ {
944
+ value: optionPresentation.commandPrefix,
945
+ testId: "agent-interactive-command-prefix-option"
946
+ }
947
+ ) : null,
948
+ option.description ? /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionDescription, children: option.description }) : null,
949
+ keyboardShortcuts && shortcutLabel && !showSpinner ? /* @__PURE__ */ jsx4(
950
+ ShortcutBadge,
951
+ {
952
+ className: AgentGUIConversation_styles_default.interactiveOptionShortcut,
953
+ "aria-hidden": "true",
954
+ children: shortcutLabel
955
+ }
956
+ ) : null,
957
+ showSpinner ? /* @__PURE__ */ jsx4(InteractiveOptionSpinner, {}) : null
958
+ ]
959
+ },
960
+ option.id
961
+ );
962
+ }) })
963
+ ] }) });
964
+ }
965
+ function ExitPlanPromptSurface({
966
+ prompt,
967
+ embedded = false,
968
+ edgeGlow = false,
969
+ isSubmitting,
970
+ onSubmit,
971
+ labels
972
+ }) {
973
+ "use memo";
974
+ const [feedback, setFeedback] = useState("");
975
+ const [submittingOptionId, setSubmittingOptionId] = useState(
976
+ null
977
+ );
978
+ const trimmed = feedback.trim();
979
+ const continueLabel = trimmed === "" ? labels.stayInPlan : labels.sendFeedback;
980
+ useEffect2(() => {
981
+ setSubmittingOptionId(null);
982
+ }, [prompt.requestId]);
983
+ return /* @__PURE__ */ jsx4("section", { className: interactivePromptClassName(embedded), children: /* @__PURE__ */ jsxs2("div", { className: interactivePromptCardClassName(edgeGlow), children: [
984
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptLead, children: stripPromptTitlePunctuation(labels.planLead) }),
985
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptOptions, children: labels.planModes.map((mode) => {
986
+ const showSpinner = submittingOptionId === mode.id;
987
+ return /* @__PURE__ */ jsxs2(
988
+ "button",
989
+ {
990
+ type: "button",
991
+ className: AgentGUIConversation_styles_default.interactiveOptionButton,
992
+ "aria-label": interactiveOptionLabel(
993
+ mode.label,
994
+ mode.description
995
+ ),
996
+ disabled: isSubmitting || submittingOptionId !== null,
997
+ onClick: () => {
998
+ setSubmittingOptionId(mode.id);
999
+ onSubmit({
1000
+ requestId: prompt.requestId,
1001
+ action: "allow",
1002
+ optionId: mode.id
1003
+ });
1004
+ },
1005
+ children: [
1006
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionTitle, children: mode.label }),
1007
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionDescription, children: mode.description }),
1008
+ showSpinner ? /* @__PURE__ */ jsx4(InteractiveOptionSpinner, {}) : null
1009
+ ]
1010
+ },
1011
+ mode.id
1012
+ );
1013
+ }) }),
1014
+ /* @__PURE__ */ jsxs2("div", { className: AgentGUIConversation_styles_default.interactivePromptFooter, children: [
1015
+ /* @__PURE__ */ jsx4(
1016
+ "textarea",
1017
+ {
1018
+ value: feedback,
1019
+ placeholder: labels.feedbackPlaceholder,
1020
+ disabled: isSubmitting,
1021
+ className: AgentGUIConversation_styles_default.interactivePromptTextarea,
1022
+ onChange: (event) => setFeedback(event.currentTarget.value)
1023
+ }
1024
+ ),
1025
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptActions, children: /* @__PURE__ */ jsx4(
1026
+ "button",
1027
+ {
1028
+ type: "button",
1029
+ disabled: isSubmitting,
1030
+ onClick: () => onSubmit({
1031
+ requestId: prompt.requestId,
1032
+ action: "deny",
1033
+ payload: trimmed ? { denyMessage: trimmed } : void 0
1034
+ }),
1035
+ children: continueLabel
1036
+ }
1037
+ ) })
1038
+ ] })
1039
+ ] }) });
1040
+ }
1041
+ function AskUserPromptSurface({
1042
+ prompt,
1043
+ embedded = false,
1044
+ edgeGlow = false,
1045
+ isSubmitting,
1046
+ onSubmit,
1047
+ labels
1048
+ }) {
1049
+ "use memo";
1050
+ const [index, setIndex] = useState(0);
1051
+ const [selectedByQuestionId, setSelectedByQuestionId] = useState({});
1052
+ const [freeTextByQuestionId, setFreeTextByQuestionId] = useState({});
1053
+ const question = prompt.questions[index] ?? null;
1054
+ const selected = question ? selectedByQuestionId[question.id] ?? [] : [];
1055
+ const freeText = question ? freeTextByQuestionId[question.id] ?? "" : "";
1056
+ const canAdvance = question !== null && (selected.length > 0 || freeText.trim() !== "" || question.options.length === 0);
1057
+ const isLast = index >= prompt.questions.length - 1;
1058
+ const payload = useMemo(() => {
1059
+ const answersByQuestionId = {};
1060
+ const answers = [];
1061
+ for (const current of prompt.questions) {
1062
+ const chosen = selectedByQuestionId[current.id] ?? [];
1063
+ const other = (freeTextByQuestionId[current.id] ?? "").trim();
1064
+ if (current.multiSelect) {
1065
+ const value2 = other ? [...chosen, other] : chosen;
1066
+ if (value2.length > 0) {
1067
+ answersByQuestionId[current.id] = value2;
1068
+ answers.push(value2.join(", "));
1069
+ }
1070
+ continue;
1071
+ }
1072
+ const value = other || chosen[0];
1073
+ if (value) {
1074
+ answersByQuestionId[current.id] = value;
1075
+ answers.push(value);
1076
+ }
1077
+ }
1078
+ return { answers, answersByQuestionId };
1079
+ }, [freeTextByQuestionId, prompt.questions, selectedByQuestionId]);
1080
+ if (!question) {
1081
+ return /* @__PURE__ */ jsx4("section", { className: interactivePromptClassName(embedded), children: /* @__PURE__ */ jsx4("div", { className: interactivePromptCardClassName(edgeGlow), children: /* @__PURE__ */ jsxs2(
1082
+ "div",
1083
+ {
1084
+ className: `${AgentGUIConversation_styles_default.interactivePromptLead} inline-flex items-center gap-1.5`,
1085
+ children: [
1086
+ /* @__PURE__ */ jsx4(
1087
+ MessageSquareMoreIcon,
1088
+ {
1089
+ size: 15,
1090
+ active: true,
1091
+ "aria-hidden": "true",
1092
+ className: "shrink-0"
1093
+ }
1094
+ ),
1095
+ stripPromptTitlePunctuation(labels.waitingForAnswer)
1096
+ ]
1097
+ }
1098
+ ) }) });
1099
+ }
1100
+ return /* @__PURE__ */ jsx4("section", { className: interactivePromptClassName(embedded), children: /* @__PURE__ */ jsxs2("div", { className: interactivePromptCardClassName(edgeGlow), children: [
1101
+ /* @__PURE__ */ jsxs2("div", { className: AgentGUIConversation_styles_default.interactivePromptHeader, children: [
1102
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactivePromptLead, children: stripPromptTitlePunctuation(question.header) }),
1103
+ /* @__PURE__ */ jsxs2("span", { className: AgentGUIConversation_styles_default.interactivePromptMeta, children: [
1104
+ index + 1,
1105
+ "/",
1106
+ prompt.questions.length
1107
+ ] })
1108
+ ] }),
1109
+ /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptQuestion, children: question.question }),
1110
+ question.options.length > 0 ? /* @__PURE__ */ jsx4("div", { className: AgentGUIConversation_styles_default.interactivePromptOptions, children: question.options.map((option) => {
1111
+ const active = selected.includes(option.label);
1112
+ return /* @__PURE__ */ jsxs2(
1113
+ "button",
1114
+ {
1115
+ type: "button",
1116
+ className: AgentGUIConversation_styles_default.interactiveOptionButton,
1117
+ "data-active": active,
1118
+ "aria-label": interactiveOptionLabel(
1119
+ option.label,
1120
+ option.description
1121
+ ),
1122
+ disabled: isSubmitting,
1123
+ onClick: () => {
1124
+ setSelectedByQuestionId((current) => {
1125
+ const existing = current[question.id] ?? [];
1126
+ const next = question.multiSelect ? existing.includes(option.label) ? existing.filter((value) => value !== option.label) : [...existing, option.label] : existing.includes(option.label) ? [] : [option.label];
1127
+ return { ...current, [question.id]: next };
1128
+ });
1129
+ },
1130
+ children: [
1131
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionTitle, children: option.label }),
1132
+ /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionDescription, children: option.description })
1133
+ ]
1134
+ },
1135
+ option.label
1136
+ );
1137
+ }) }) : null,
1138
+ /* @__PURE__ */ jsx4(
1139
+ "textarea",
1140
+ {
1141
+ value: freeText,
1142
+ placeholder: labels.answerPlaceholder,
1143
+ disabled: isSubmitting,
1144
+ className: AgentGUIConversation_styles_default.interactivePromptTextarea,
1145
+ onChange: (event) => {
1146
+ const value = event.currentTarget.value;
1147
+ setFreeTextByQuestionId((current) => ({
1148
+ ...current,
1149
+ [question.id]: value
1150
+ }));
1151
+ }
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ jsxs2("div", { className: AgentGUIConversation_styles_default.interactivePromptActions, children: [
1155
+ /* @__PURE__ */ jsx4(
1156
+ "button",
1157
+ {
1158
+ type: "button",
1159
+ disabled: isSubmitting || index === 0,
1160
+ onClick: () => setIndex((current) => Math.max(current - 1, 0)),
1161
+ children: labels.previousQuestion
1162
+ }
1163
+ ),
1164
+ isLast ? /* @__PURE__ */ jsx4(
1165
+ "button",
1166
+ {
1167
+ type: "button",
1168
+ disabled: isSubmitting || Object.keys(payload.answersByQuestionId).length === 0,
1169
+ onClick: () => onSubmit({
1170
+ requestId: prompt.requestId,
1171
+ action: "submit",
1172
+ payload
1173
+ }),
1174
+ children: labels.submitAnswers
1175
+ }
1176
+ ) : /* @__PURE__ */ jsx4(
1177
+ "button",
1178
+ {
1179
+ type: "button",
1180
+ disabled: isSubmitting || !canAdvance,
1181
+ onClick: () => setIndex(
1182
+ (current) => Math.min(current + 1, prompt.questions.length - 1)
1183
+ ),
1184
+ children: labels.nextQuestion
1185
+ }
1186
+ )
1187
+ ] })
1188
+ ] }) });
1189
+ }
1190
+ function isEnterLikeKey(event) {
1191
+ return event.key === "Enter" || event.code === "Enter" || event.code === "NumpadEnter";
1192
+ }
1193
+ function isEditableKeyboardTarget(target) {
1194
+ if (!(target instanceof HTMLElement)) {
1195
+ return false;
1196
+ }
1197
+ const tagName = target.tagName.toLowerCase();
1198
+ return tagName === "input" || tagName === "textarea" || tagName === "select" || target.isContentEditable;
1199
+ }
1200
+ function approvalOptionShortcutLabel(optionIndex, isDarwin) {
1201
+ if (optionIndex === 0) {
1202
+ return translate("agentHost.agentGui.shortcutEnter");
1203
+ }
1204
+ if (optionIndex === 1) {
1205
+ return isDarwin ? translate("agentHost.agentGui.shortcutCmdEnter") : translate("agentHost.agentGui.shortcutCtrEnter");
1206
+ }
1207
+ return null;
1208
+ }
1209
+ function isDarwinPlatform(platform) {
1210
+ if (platform) {
1211
+ return platform === "darwin";
1212
+ }
1213
+ if (typeof navigator === "undefined") {
1214
+ return false;
1215
+ }
1216
+ const userAgentPlatform = "userAgentData" in navigator ? navigator.userAgentData?.platform : void 0;
1217
+ const navigatorPlatform = userAgentPlatform ?? navigator.platform ?? "";
1218
+ return /mac/i.test(navigatorPlatform);
1219
+ }
1220
+ function InteractiveOptionSpinner() {
1221
+ "use memo";
1222
+ return /* @__PURE__ */ jsx4(
1223
+ Spinner,
1224
+ {
1225
+ className: AgentGUIConversation_styles_default.interactiveOptionSpinner,
1226
+ testId: "agent-interactive-option-spinner"
1227
+ }
1228
+ );
1229
+ }
1230
+ function SendFilledIcon() {
1231
+ "use memo";
1232
+ return /* @__PURE__ */ jsx4(
1233
+ "svg",
1234
+ {
1235
+ width: "24",
1236
+ height: "24",
1237
+ viewBox: "0 0 24 24",
1238
+ fill: "none",
1239
+ "aria-hidden": "true",
1240
+ children: /* @__PURE__ */ jsx4(
1241
+ "path",
1242
+ {
1243
+ d: "M2.74311 8.80587C2.84592 8.40096 3.14571 8.08844 3.54551 7.97033L18.5197 3.51569C18.9336 3.39383 19.3809 3.5054 19.6881 3.81262C19.9951 4.11984 20.1076 4.56798 19.9857 4.9817L15.5311 19.9559C15.413 20.3557 15.1005 20.6555 14.6956 20.7583C14.2895 20.8597 13.869 20.7438 13.5721 20.4469L10.455 15.1823C10.8585 14.6483 12.1563 12.9094 14.3475 9.96528C14.6086 9.70419 14.6382 9.31168 14.4138 9.08692C14.1891 8.86221 13.796 8.8913 13.5348 9.15252L8.31088 13.0423L3.05316 9.92799C2.7562 9.63104 2.64049 9.21071 2.74311 8.80587Z",
1244
+ fill: "currentColor"
1245
+ }
1246
+ )
1247
+ }
1248
+ );
1249
+ }
1250
+ function interactivePromptClassName(embedded) {
1251
+ return embedded ? `${AgentGUIConversation_styles_default.interactivePrompt} agent-gui-conversation__interactive-prompt--embedded` : AgentGUIConversation_styles_default.interactivePrompt;
1252
+ }
1253
+ function interactivePromptCardClassName(edgeGlow) {
1254
+ return edgeGlow ? `${AgentGUIConversation_styles_default.interactivePromptCard} agent-gui-edge-glow` : AgentGUIConversation_styles_default.interactivePromptCard;
1255
+ }
1256
+ function formatToolDetails(input) {
1257
+ return getPromptToolDetails(input).map((detail) => ({
1258
+ kind: detail.kind,
1259
+ label: promptToolDetailLabel(detail.kind),
1260
+ value: detail.value,
1261
+ ...detail.meta ? { meta: detail.meta } : {}
1262
+ }));
1263
+ }
1264
+ function PromptDetailValue({
1265
+ detail
1266
+ }) {
1267
+ "use memo";
1268
+ if (detail.kind !== "command") {
1269
+ return /* @__PURE__ */ jsx4("span", { className: AgentGUIConversation_styles_default.interactiveOptionDescription, children: detail.value });
1270
+ }
1271
+ return /* @__PURE__ */ jsx4(
1272
+ CommandTextWithTooltip,
1273
+ {
1274
+ value: detail.value,
1275
+ testId: "agent-interactive-command-detail"
1276
+ }
1277
+ );
1278
+ }
1279
+ function CommandTextWithTooltip({
1280
+ value,
1281
+ testId
1282
+ }) {
1283
+ "use memo";
1284
+ return /* @__PURE__ */ jsx4(TooltipProvider, { delayDuration: COMMAND_TOOLTIP_DELAY_MS, children: /* @__PURE__ */ jsxs2(Tooltip, { children: [
1285
+ /* @__PURE__ */ jsx4(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx4(
1286
+ "span",
1287
+ {
1288
+ className: `${AgentGUIConversation_styles_default.interactiveOptionDescription} ${AgentGUIConversation_styles_default.interactiveOptionCommandDescription}`,
1289
+ "data-agent-interactive-command-detail": testId === "agent-interactive-command-detail" ? "true" : void 0,
1290
+ "data-agent-interactive-command-prefix-option": testId === "agent-interactive-command-prefix-option" ? "true" : void 0,
1291
+ children: value
1292
+ }
1293
+ ) }),
1294
+ /* @__PURE__ */ jsx4(TooltipContent, { className: AgentGUIConversation_styles_default.interactiveOptionCommandTooltip, children: value })
1295
+ ] }) });
1296
+ }
1297
+ function promptToolDetailLabel(kind) {
1298
+ switch (kind) {
1299
+ case "command":
1300
+ return translate("agentHost.agentTool.details.command");
1301
+ case "path":
1302
+ return translate("agentHost.agentTool.details.path");
1303
+ case "query":
1304
+ return translate("agentHost.agentTool.details.query");
1305
+ }
1306
+ }
1307
+ function filterDuplicatePromptTitleDetails(details, title) {
1308
+ const normalizedTitle = normalizePromptDetailText(title);
1309
+ if (!normalizedTitle) {
1310
+ return details;
1311
+ }
1312
+ return details.filter(
1313
+ (detail) => normalizePromptDetailText(detail.value) !== normalizedTitle
1314
+ );
1315
+ }
1316
+ function normalizePromptDetailText(value) {
1317
+ return value?.trim() ?? "";
1318
+ }
1319
+ function isApprovalFeedbackOption(option) {
1320
+ return isDenyApprovalOptionToken(option.id) || isDenyApprovalOptionToken(option.kind);
1321
+ }
1322
+ function approvalFeedbackOptionId(options) {
1323
+ const explicitFeedbackOption = options.find(
1324
+ (option) => isExplicitFeedbackDenyApprovalOption(option)
1325
+ );
1326
+ if (explicitFeedbackOption) {
1327
+ return explicitFeedbackOption.id;
1328
+ }
1329
+ return options.find(isApprovalFeedbackOption)?.id ?? null;
1330
+ }
1331
+ function isExplicitFeedbackDenyApprovalOption(option) {
1332
+ for (const value of [option.id, option.kind]) {
1333
+ switch (normalizeApprovalOptionToken(value ?? "")) {
1334
+ case "abort":
1335
+ case "cancel":
1336
+ case "cancelled":
1337
+ case "canceled":
1338
+ case "denywithfeedback":
1339
+ case "rejectwithfeedback":
1340
+ return true;
1341
+ default:
1342
+ break;
1343
+ }
1344
+ }
1345
+ return false;
1346
+ }
1347
+ function isDenyApprovalOptionToken(value) {
1348
+ switch (normalizeApprovalOptionToken(value ?? "")) {
1349
+ case "abort":
1350
+ case "cancel":
1351
+ case "cancelled":
1352
+ case "canceled":
1353
+ case "deny":
1354
+ case "denied":
1355
+ case "reject":
1356
+ case "rejected":
1357
+ case "rejectonce":
1358
+ case "disallow":
1359
+ case "decline":
1360
+ case "declined":
1361
+ case "no":
1362
+ return true;
1363
+ default:
1364
+ return false;
1365
+ }
1366
+ }
1367
+ function approvalPromptTitle(value, detailCount) {
1368
+ const title = value.trim();
1369
+ return title && detailCount === 0 && !isPromptRequestIdTitle(title) ? title : null;
1370
+ }
1371
+ function stripPromptTitlePunctuation(value) {
1372
+ return value.trim().replace(/[.。]+$/u, "");
1373
+ }
1374
+ function interactiveOptionLabel(label, description) {
1375
+ const trimmedDescription = description?.trim();
1376
+ return trimmedDescription ? `${label} ${trimmedDescription}` : label;
1377
+ }
1378
+
1379
+ // shared/AgentMessageMarkdown.tsx
1380
+ import {
1381
+ createContext as createContext3,
1382
+ startTransition,
1383
+ useCallback as useCallback3,
1384
+ useEffect as useEffect3,
1385
+ useContext as useContext3,
1386
+ useMemo as useMemo2,
1387
+ useState as useState2
1388
+ } from "react";
1389
+
1390
+ // app/renderer/components/ZoomableImage.tsx
1391
+ import {
1392
+ cloneElement
1393
+ } from "react";
1394
+ import Zoom from "react-medium-image-zoom";
1395
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1396
+ function ZoomableImage({
1397
+ className,
1398
+ wrapElement = "div",
1399
+ ...props
1400
+ }) {
1401
+ const { t } = useTranslation();
1402
+ const renderZoomContent = ({
1403
+ buttonUnzoom,
1404
+ img
1405
+ }) => /* @__PURE__ */ jsxs3(Fragment, { children: [
1406
+ img,
1407
+ cloneElement(buttonUnzoom, {
1408
+ className: cn(
1409
+ buttonUnzoom.props.className,
1410
+ "nodrag tsh-desktop-no-drag"
1411
+ )
1412
+ })
1413
+ ] });
1414
+ return /* @__PURE__ */ jsx5(
1415
+ Zoom,
1416
+ {
1417
+ a11yNameButtonZoom: t("common.expandImage"),
1418
+ a11yNameButtonUnzoom: t("common.minimizeImage"),
1419
+ classDialog: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
1420
+ wrapElement,
1421
+ zoomMargin: 24,
1422
+ ZoomContent: renderZoomContent,
1423
+ children: /* @__PURE__ */ jsx5(
1424
+ "img",
1425
+ {
1426
+ ...props,
1427
+ className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
1428
+ }
1429
+ )
1430
+ }
1431
+ );
1432
+ }
1433
+
1434
+ // shared/AgentMessageMarkdown.tsx
1435
+ import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
1436
+ import rehypeSanitize, {
1437
+ defaultSchema
1438
+ } from "rehype-sanitize";
1439
+ import remarkGfm from "remark-gfm";
1440
+ import {
1441
+ resolveWorkspaceFileExtension,
1442
+ resolveWorkspaceImageMimeType,
1443
+ workspaceFileName as basenameWorkspacePath
1444
+ } from "@tutti-os/workspace-file-manager/services";
1445
+
1446
+ // shared/utils/websiteUrl.ts
1447
+ var ALLOWED_WEBSITE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
1448
+ var LIKELY_HOST_PATTERN = /^(localhost|(\d{1,3}\.){3}\d{1,3}|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
1449
+ var EXPLICIT_PROTOCOL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//;
1450
+ var LOOPBACK_HOST_PATTERN = /^(localhost|127(?:\.\d{1,3}){3})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
1451
+ function defaultSchemeForHostInput(value) {
1452
+ return LOOPBACK_HOST_PATTERN.test(value) ? "http" : "https";
1453
+ }
1454
+ function resolveWebsiteNavigationUrl(rawUrl) {
1455
+ const trimmed = rawUrl.trim();
1456
+ if (trimmed.length === 0) {
1457
+ return { url: null, error: null };
1458
+ }
1459
+ if (EXPLICIT_PROTOCOL_PATTERN.test(trimmed)) {
1460
+ try {
1461
+ const parsed = new URL(trimmed);
1462
+ if (!ALLOWED_WEBSITE_PROTOCOLS.has(parsed.protocol)) {
1463
+ return { url: null, error: `Unsupported protocol: ${parsed.protocol}` };
1464
+ }
1465
+ return { url: parsed.toString(), error: null };
1466
+ } catch {
1467
+ return { url: null, error: "Invalid URL" };
1468
+ }
1469
+ }
1470
+ if (!LIKELY_HOST_PATTERN.test(trimmed)) {
1471
+ return { url: null, error: "Invalid URL" };
1472
+ }
1473
+ try {
1474
+ const parsed = new URL(
1475
+ `${defaultSchemeForHostInput(trimmed)}://${trimmed}`
1476
+ );
1477
+ return { url: parsed.toString(), error: null };
1478
+ } catch {
1479
+ return { url: null, error: "Invalid URL" };
1480
+ }
1481
+ }
1482
+
1483
+ // actions/workspaceLinkActions.ts
1484
+ import {
1485
+ parseWorkspaceIssueMentionHref
1486
+ } from "@tutti-os/workspace-issue-manager/core";
1487
+ var URL_LIKE_LINK_PATTERN = /^[a-zA-Z][a-zA-Z\d+.-]*:|^#/;
1488
+ function resolveWorkspaceFilePathCandidate({
1489
+ path,
1490
+ workspaceRoot,
1491
+ basePath
1492
+ }) {
1493
+ const rawPath = decodeWorkspaceLinkPath(path.trim());
1494
+ const root = normalizeWorkspaceFilePath(workspaceRoot?.trim() ?? "");
1495
+ if (!rawPath || !root || isUrlLikeWorkspaceFilePath(rawPath)) {
1496
+ return null;
1497
+ }
1498
+ const normalizedPath = normalizeWorkspaceFilePath(rawPath);
1499
+ const base = normalizeWorkspaceFilePath(basePath?.trim() || root);
1500
+ const resolvedPath = isAbsoluteLocalPath(normalizedPath) ? normalizedPath : normalizeWorkspaceFilePath(`${base}/${normalizedPath}`);
1501
+ if (!isInsideOrEqual(resolvedPath, root) && !isDirectAgentGeneratedImagePath(resolvedPath)) {
1502
+ return null;
1503
+ }
1504
+ return {
1505
+ path: resolvedPath,
1506
+ directoryPath: resolvedPath === root ? root : dirname(resolvedPath),
1507
+ workspaceRoot: root
1508
+ };
1509
+ }
1510
+ function resolveWorkspaceFileLinkAction({
1511
+ path,
1512
+ workspaceRoot,
1513
+ basePath,
1514
+ source
1515
+ }) {
1516
+ const candidate = resolveWorkspaceFilePathCandidate({
1517
+ path,
1518
+ workspaceRoot,
1519
+ basePath
1520
+ });
1521
+ if (!candidate) {
1522
+ return null;
1523
+ }
1524
+ return {
1525
+ type: "open-workspace-file",
1526
+ path: candidate.path,
1527
+ directoryPath: candidate.directoryPath,
1528
+ workspaceRoot: candidate.workspaceRoot,
1529
+ source
1530
+ };
1531
+ }
1532
+ function resolveWorkspaceUrlLinkAction({
1533
+ url,
1534
+ source
1535
+ }) {
1536
+ const resolved = resolveWebsiteNavigationUrl(url);
1537
+ if (!resolved.url || resolved.error) {
1538
+ return null;
1539
+ }
1540
+ return {
1541
+ type: "open-url",
1542
+ url: resolved.url,
1543
+ source
1544
+ };
1545
+ }
1546
+ function resolveWorkspaceMentionLinkAction({
1547
+ href,
1548
+ source
1549
+ }) {
1550
+ const rawHref = href.trim();
1551
+ if (!rawHref.toLowerCase().startsWith("mention://")) {
1552
+ return null;
1553
+ }
1554
+ let url;
1555
+ try {
1556
+ url = new URL(rawHref);
1557
+ } catch {
1558
+ return null;
1559
+ }
1560
+ const workspaceId = url.searchParams.get("workspaceId")?.trim() || "";
1561
+ const targetId = url.searchParams.get("id")?.trim() || "";
1562
+ if (!workspaceId || !targetId) {
1563
+ return null;
1564
+ }
1565
+ if (url.hostname === "agent-session") {
1566
+ const provider = url.searchParams.get("provider")?.trim() || null;
1567
+ return {
1568
+ type: "open-agent-session",
1569
+ workspaceId,
1570
+ agentSessionId: targetId,
1571
+ ...provider ? { provider } : {},
1572
+ source
1573
+ };
1574
+ }
1575
+ if (url.hostname === "workspace-issue") {
1576
+ const parsedIssueMention = parseWorkspaceIssueMentionHref(rawHref);
1577
+ if (!parsedIssueMention) {
1578
+ return null;
1579
+ }
1580
+ return {
1581
+ type: "open-workspace-issue",
1582
+ workspaceId: parsedIssueMention.workspaceId,
1583
+ issueId: parsedIssueMention.issueId,
1584
+ ...parsedIssueMention.mode ? { mode: parsedIssueMention.mode } : {},
1585
+ ...parsedIssueMention.outputDir ? { outputDir: parsedIssueMention.outputDir } : {},
1586
+ ...parsedIssueMention.runId ? { runId: parsedIssueMention.runId } : {},
1587
+ ...parsedIssueMention.taskId ? { taskId: parsedIssueMention.taskId } : {},
1588
+ ...parsedIssueMention.topicId ? { topicId: parsedIssueMention.topicId } : {},
1589
+ source
1590
+ };
1591
+ }
1592
+ return null;
1593
+ }
1594
+ function resolveWorkspaceLinkAction({
1595
+ href,
1596
+ workspaceRoot,
1597
+ basePath,
1598
+ source
1599
+ }) {
1600
+ return resolveWorkspaceMentionLinkAction({ href, source }) ?? resolveWorkspaceFileLinkAction({
1601
+ path: href,
1602
+ workspaceRoot,
1603
+ basePath,
1604
+ source
1605
+ }) ?? resolveWorkspaceUrlLinkAction({ url: href, source });
1606
+ }
1607
+ function normalizeWorkspaceFilePath(path) {
1608
+ const normalizedPath = path.trim().replaceAll("\\", "/");
1609
+ const drive = /^[A-Za-z]:/.exec(normalizedPath)?.[0] ?? "";
1610
+ const startsWithSlash = normalizedPath.startsWith("/");
1611
+ const pathBody = drive ? normalizedPath.slice(drive.length) : startsWithSlash ? normalizedPath.slice(1) : normalizedPath;
1612
+ const parts = [];
1613
+ for (const part of pathBody.split("/")) {
1614
+ if (!part || part === ".") {
1615
+ continue;
1616
+ }
1617
+ if (part === "..") {
1618
+ parts.pop();
1619
+ continue;
1620
+ }
1621
+ parts.push(part);
1622
+ }
1623
+ if (drive) {
1624
+ return parts.length > 0 ? `${drive}/${parts.join("/")}` : `${drive}/`;
1625
+ }
1626
+ if (startsWithSlash) {
1627
+ return parts.length > 0 ? `/${parts.join("/")}` : "/";
1628
+ }
1629
+ return parts.join("/");
1630
+ }
1631
+ function isUrlLikeWorkspaceFilePath(path) {
1632
+ if (path.startsWith("#")) {
1633
+ return true;
1634
+ }
1635
+ if (isWindowsAbsolutePath(path.trim().replaceAll("\\", "/"))) {
1636
+ return false;
1637
+ }
1638
+ return URL_LIKE_LINK_PATTERN.test(path);
1639
+ }
1640
+ function isAbsoluteLocalPath(path) {
1641
+ return path.startsWith("/") || isWindowsAbsolutePath(path);
1642
+ }
1643
+ function isWindowsAbsolutePath(path) {
1644
+ return /^[A-Za-z]:\//.test(path);
1645
+ }
1646
+ function decodeWorkspaceLinkPath(path) {
1647
+ if (!path.includes("%")) {
1648
+ return path;
1649
+ }
1650
+ try {
1651
+ return decodeURI(path);
1652
+ } catch {
1653
+ return path;
1654
+ }
1655
+ }
1656
+ function dirname(path) {
1657
+ const index = path.lastIndexOf("/");
1658
+ if (index <= 0) {
1659
+ return "/";
1660
+ }
1661
+ return path.slice(0, index);
1662
+ }
1663
+ function isInsideOrEqual(path, root) {
1664
+ if (root === "/") {
1665
+ return path.startsWith("/");
1666
+ }
1667
+ const comparison = isWindowsAbsolutePath(root) || isWindowsAbsolutePath(path) ? { path: path.toLowerCase(), root: root.toLowerCase() } : { path, root };
1668
+ return comparison.path === comparison.root || comparison.path.startsWith(`${comparison.root}/`);
1669
+ }
1670
+ function isDirectAgentGeneratedImagePath(path) {
1671
+ if (!isAbsoluteLocalPath(path)) {
1672
+ return false;
1673
+ }
1674
+ const segments = path.split("/").filter(Boolean);
1675
+ const stateRootIndex = segments.findIndex(
1676
+ (segment) => segment === ".nextop" || segment === ".nextop-dev"
1677
+ );
1678
+ if (stateRootIndex < 0) {
1679
+ return false;
1680
+ }
1681
+ const statePath = segments.slice(stateRootIndex);
1682
+ if (statePath[1] !== "agent" || statePath[2] !== "runs" || !statePath.includes("generated_images")) {
1683
+ return false;
1684
+ }
1685
+ return /\.(?:png|jpe?g|gif|webp|bmp)$/i.test(path);
1686
+ }
1687
+
1688
+ // shared/AgentMessageMarkdown.tsx
1689
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1690
+ var COLLAPSED_LINE_LIMIT = 8;
1691
+ var APPROX_CHARS_PER_LINE = 34;
1692
+ var DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD = 4096;
1693
+ var DEFERRED_LONG_MARKDOWN_FALLBACK_DELAY_MS = 80;
1694
+ var DEFERRED_LONG_MARKDOWN_IDLE_TIMEOUT_MS = 700;
1695
+ var PLAIN_SESSION_MENTION_AGENT_LABELS = [
1696
+ "Claude Code",
1697
+ "Nexight",
1698
+ "Codex"
1699
+ ];
1700
+ var MARKDOWN_SANITIZE_SCHEMA = {
1701
+ ...defaultSchema,
1702
+ protocols: {
1703
+ ...defaultSchema.protocols,
1704
+ href: [...defaultSchema.protocols?.href ?? [], "mention"]
1705
+ }
1706
+ };
1707
+ var EMPTY_WORKSPACE_APP_ICONS = [];
1708
+ var MarkdownLinkContext = createContext3(false);
1709
+ function AgentMessageMarkdown({
1710
+ content,
1711
+ onLinkClick,
1712
+ onLinkAction,
1713
+ workspaceLinkContext = null,
1714
+ workspaceAppIcons = EMPTY_WORKSPACE_APP_ICONS,
1715
+ collapsible = false,
1716
+ expandLabel,
1717
+ className,
1718
+ inline = false,
1719
+ normalizePlainIssueMentionTitle = false,
1720
+ deferLongContentRender = false,
1721
+ enableImageZoom = false
1722
+ }) {
1723
+ "use memo";
1724
+ const { t } = useTranslation();
1725
+ const workspaceRoot = workspaceLinkContext?.workspaceRoot ?? null;
1726
+ const basePath = workspaceLinkContext?.basePath ?? null;
1727
+ const workspaceLinkSource = workspaceLinkContext?.source ?? null;
1728
+ const [isExpanded, setIsExpanded] = useState2(false);
1729
+ const resolvedExpandLabel = expandLabel ?? t("agentHost.workspaceAgentMessageExpand");
1730
+ const shouldCollapse = collapsible && isLikelyLongerThanLineLimit(content);
1731
+ const isCollapsed = shouldCollapse && !isExpanded;
1732
+ const ContainerTag = inline ? "span" : "div";
1733
+ const contentSignature = useMemo2(
1734
+ () => hashMarkdownProfilerContent(content),
1735
+ [content]
1736
+ );
1737
+ const normalizedContent = useMemo2(
1738
+ () => linkBareLocalAbsolutePaths(
1739
+ normalizeMentionMarkdownLinks(
1740
+ normalizePlainIssueMentionTitle ? normalizePlainIssueMentionTitleContent(
1741
+ normalizePlainSessionMentionTitle(content)
1742
+ ) : normalizePlainSessionMentionTitle(content)
1743
+ )
1744
+ ),
1745
+ [content, normalizePlainIssueMentionTitle]
1746
+ );
1747
+ const isMentionOnly = isMentionOnlyMarkdownContent(normalizedContent);
1748
+ const shouldDeferMarkdownRender = deferLongContentRender && !inline && content.length >= DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD && !isExpanded;
1749
+ const markdownRenderReady = useDeferredMarkdownRenderReady(
1750
+ contentSignature,
1751
+ shouldDeferMarkdownRender
1752
+ );
1753
+ const handleLinkClick = useCallback3(
1754
+ (href) => {
1755
+ if (workspaceLinkSource && onLinkAction) {
1756
+ const action = resolveWorkspaceLinkAction({
1757
+ href,
1758
+ workspaceRoot,
1759
+ basePath,
1760
+ source: workspaceLinkSource
1761
+ });
1762
+ if (action) {
1763
+ onLinkAction(action);
1764
+ return;
1765
+ }
1766
+ }
1767
+ onLinkClick?.(href);
1768
+ },
1769
+ [basePath, onLinkAction, onLinkClick, workspaceLinkSource, workspaceRoot]
1770
+ );
1771
+ const handleAnchorClickCapture = useCallback3(
1772
+ (event) => {
1773
+ const href = resolveMarkdownAnchorHref(event.target);
1774
+ if (!href) {
1775
+ return;
1776
+ }
1777
+ event.preventDefault();
1778
+ event.stopPropagation();
1779
+ handleLinkClick(href);
1780
+ },
1781
+ [handleLinkClick]
1782
+ );
1783
+ const markdownComponents = useMemo2(
1784
+ () => ({
1785
+ a: (props) => /* @__PURE__ */ jsx6(
1786
+ MarkdownLink,
1787
+ {
1788
+ ...props,
1789
+ onLinkClick: handleLinkClick,
1790
+ workspaceAppIcons
1791
+ }
1792
+ ),
1793
+ code: (props) => /* @__PURE__ */ jsx6(MarkdownCode, { ...props, onLinkClick: handleLinkClick }),
1794
+ img: (props) => /* @__PURE__ */ jsx6(MarkdownImage, { ...props, enableZoom: enableImageZoom }),
1795
+ p: (props) => /* @__PURE__ */ jsx6(MarkdownParagraph, { ...props, inline }),
1796
+ ul: MarkdownUnorderedList,
1797
+ ol: MarkdownOrderedList,
1798
+ li: MarkdownListItem
1799
+ }),
1800
+ [enableImageZoom, handleLinkClick, inline, workspaceAppIcons]
1801
+ );
1802
+ return /* @__PURE__ */ jsxs4(
1803
+ ContainerTag,
1804
+ {
1805
+ className: "flex w-full min-w-0 flex-col items-start gap-1",
1806
+ "data-workspace-agent-markdown-shell": "true",
1807
+ children: [
1808
+ /* @__PURE__ */ jsx6(
1809
+ ContainerTag,
1810
+ {
1811
+ className: cn(
1812
+ "relative w-full min-w-0 overflow-x-auto text-[13px] leading-[1.5] text-[var(--text-primary)] [overflow-wrap:anywhere]",
1813
+ "[&_>table:first-child]:mt-0 [&_p]:mb-2 [&_pre]:mb-2 [&_blockquote]:mb-2",
1814
+ "[&_hr]:my-4 [&_hr]:h-0 [&_hr]:border-0 [&_hr]:border-t [&_hr]:border-t-[color-mix(in_srgb,var(--text-primary)_14%,transparent)]",
1815
+ "[&_ul]:my-2 [&_ul]:max-w-full [&_ul]:rounded-[8px] [&_ul]:border [&_ul]:border-[var(--line-2)] [&_ul]:bg-[var(--background-panel)] [&_ul]:px-4 [&_ul]:py-2",
1816
+ "[&_ol]:my-2 [&_ol]:max-w-full [&_ol]:rounded-[8px] [&_ol]:border [&_ol]:border-[var(--line-2)] [&_ol]:bg-[var(--background-panel)] [&_ol]:px-4 [&_ol]:py-2",
1817
+ "[&_li>ul]:mt-1.5 [&_li>ul]:mb-0.5 [&_li>ul]:border-0 [&_li>ul]:px-0 [&_li>ul]:py-1",
1818
+ "[&_li>ol]:mt-1.5 [&_li>ol]:mb-0.5 [&_li>ol]:border-0 [&_li>ol]:px-0 [&_li>ol]:py-1",
1819
+ "[&_table]:my-2 [&_table]:w-max [&_table]:min-w-full [&_table]:max-w-full [&_table]:border-separate [&_table]:border-spacing-0 [&_table]:overflow-hidden [&_table]:rounded-[8px] [&_table]:border [&_table]:border-[var(--line-2)] [&_table]:text-[13px] [&_table]:leading-[1.45]",
1820
+ "[&_th]:max-w-[280px] [&_th]:border-r [&_th]:border-b [&_th]:border-[var(--line-2)] [&_th]:px-2 [&_th]:py-1.5 [&_th]:align-top [&_th]:font-semibold [&_th]:text-[var(--text-primary)] [&_th]:[overflow-wrap:anywhere] [&_th]:bg-[color-mix(in_srgb,var(--background-panel)_94%,var(--text-primary))]",
1821
+ "[&_td]:max-w-[280px] [&_td]:border-r [&_td]:border-b [&_td]:border-[var(--line-2)] [&_td]:px-2 [&_td]:py-1.5 [&_td]:align-top [&_td]:[overflow-wrap:anywhere]",
1822
+ "[&_tr:last-child_th]:border-b-0 [&_tr:last-child_td]:border-b-0 [&_th:last-child]:border-r-0 [&_td:last-child]:border-r-0",
1823
+ "[&_a]:cursor-pointer [&_a]:font-semibold [&_a]:text-[var(--tutti-purple)] [&_a]:no-underline [&_a:hover]:underline [&_a:focus-visible]:underline",
1824
+ "[&_strong]:font-semibold",
1825
+ "[&_code]:inline [&_code]:rounded-[2px] [&_code]:bg-[var(--transparency-block)] [&_code]:px-1 [&_code]:py-[1px] [&_code]:font-[var(--tsh-font-mono)] [&_code]:text-[11px] [&_code]:leading-[1.35] [&_code]:text-[var(--text-primary)] [&_code]:[box-decoration-break:clone] [&_code]:[-webkit-box-decoration-break:clone] [&_code]:[overflow-wrap:anywhere] [&_code]:[word-break:break-word]",
1826
+ "[&_pre]:box-border [&_pre]:overflow-auto [&_pre]:rounded-[6px] [&_pre]:bg-[var(--transparency-block)] [&_pre]:px-2.5 [&_pre]:py-2",
1827
+ "[&_pre_code]:inline [&_pre_code]:h-auto [&_pre_code]:items-normal [&_pre_code]:rounded-none [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-[inherit] [&_pre_code]:leading-[inherit] [&_pre_code]:[white-space:pre-wrap] [&_pre_code]:[overflow-wrap:anywhere] [&_pre_code]:[word-break:break-word]",
1828
+ "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
1829
+ inline && "inline min-w-0 overflow-hidden align-baseline [&_p]:inline [&_p]:m-0",
1830
+ shouldCollapse && "overflow-hidden transition-[max-height] duration-220 ease-out",
1831
+ isCollapsed && "max-h-[calc(13px*1.5*8)] overflow-hidden [mask-image:linear-gradient(180deg,black_0%,black_calc(100%_-_36px),transparent_100%)] [-webkit-mask-image:linear-gradient(180deg,black_0%,black_calc(100%_-_36px),transparent_100%)] [&_pre]:overflow-hidden",
1832
+ shouldCollapse && !isCollapsed && "max-h-[72rem]",
1833
+ className
1834
+ ),
1835
+ "data-workspace-agent-markdown": "true",
1836
+ "data-agent-mention-only": isMentionOnly ? "true" : void 0,
1837
+ "data-collapsed": isCollapsed ? "true" : "false",
1838
+ onClickCapture: handleAnchorClickCapture,
1839
+ children: markdownRenderReady ? /* @__PURE__ */ jsx6(
1840
+ ReactMarkdown,
1841
+ {
1842
+ remarkPlugins: [remarkGfm],
1843
+ rehypePlugins: [[rehypeSanitize, MARKDOWN_SANITIZE_SCHEMA]],
1844
+ urlTransform: markdownUrlTransform,
1845
+ components: markdownComponents,
1846
+ children: normalizedContent
1847
+ }
1848
+ ) : /* @__PURE__ */ jsx6(
1849
+ "div",
1850
+ {
1851
+ className: "whitespace-pre-wrap [overflow-wrap:anywhere]",
1852
+ "data-workspace-agent-markdown-deferred": "true",
1853
+ children: normalizedContent
1854
+ }
1855
+ )
1856
+ }
1857
+ ),
1858
+ shouldCollapse && !isExpanded ? /* @__PURE__ */ jsx6(
1859
+ "button",
1860
+ {
1861
+ type: "button",
1862
+ className: "m-0 border-0 bg-transparent p-0 text-[11px] leading-[1.4] font-semibold text-[var(--tutti-purple)] hover:underline focus-visible:underline focus-visible:outline-none",
1863
+ onClick: () => setIsExpanded(true),
1864
+ children: resolvedExpandLabel
1865
+ }
1866
+ ) : null
1867
+ ]
1868
+ }
1869
+ );
1870
+ }
1871
+ function resolveMarkdownAnchorHref(target) {
1872
+ if (!(target instanceof Element)) {
1873
+ return null;
1874
+ }
1875
+ const link = target.closest("[data-agent-link-href],a[href]");
1876
+ if (!(link instanceof HTMLElement)) {
1877
+ return null;
1878
+ }
1879
+ const dataHref = link.dataset.agentLinkHref?.trim();
1880
+ if (dataHref) {
1881
+ return dataHref;
1882
+ }
1883
+ if (link instanceof HTMLAnchorElement) {
1884
+ return link.getAttribute("href")?.trim() || null;
1885
+ }
1886
+ return null;
1887
+ }
1888
+ function activateMarkdownLink(event, href, onLinkClick) {
1889
+ const target = href.trim();
1890
+ if (!target) {
1891
+ return;
1892
+ }
1893
+ event.preventDefault();
1894
+ event.stopPropagation();
1895
+ onLinkClick?.(target);
1896
+ }
1897
+ function activateMarkdownLinkFromKey(event, href, onLinkClick) {
1898
+ if (event.key !== "Enter" && event.key !== " ") {
1899
+ return;
1900
+ }
1901
+ activateMarkdownLink(event, href, onLinkClick);
1902
+ }
1903
+ function activateMarkdownLinkFromPointer(event, href, onLinkClick) {
1904
+ if (event.button !== 0) {
1905
+ return;
1906
+ }
1907
+ activateMarkdownLink(event, href, onLinkClick);
1908
+ }
1909
+ function useDeferredMarkdownRenderReady(contentSignature, shouldDefer) {
1910
+ const [readySignature, setReadySignature] = useState2(
1911
+ shouldDefer ? null : contentSignature
1912
+ );
1913
+ const renderReady = !shouldDefer || readySignature === contentSignature;
1914
+ useEffect3(() => {
1915
+ if (!shouldDefer) {
1916
+ setReadySignature(contentSignature);
1917
+ return;
1918
+ }
1919
+ let canceled = false;
1920
+ let timeoutId = null;
1921
+ let idleCallbackId = null;
1922
+ const markReady = () => {
1923
+ if (canceled) {
1924
+ return;
1925
+ }
1926
+ startTransition(() => {
1927
+ setReadySignature(contentSignature);
1928
+ });
1929
+ };
1930
+ if ("requestIdleCallback" in window) {
1931
+ idleCallbackId = window.requestIdleCallback(markReady, {
1932
+ timeout: DEFERRED_LONG_MARKDOWN_IDLE_TIMEOUT_MS
1933
+ });
1934
+ } else {
1935
+ timeoutId = setTimeout(
1936
+ markReady,
1937
+ DEFERRED_LONG_MARKDOWN_FALLBACK_DELAY_MS
1938
+ );
1939
+ }
1940
+ return () => {
1941
+ canceled = true;
1942
+ if (idleCallbackId !== null) {
1943
+ window.cancelIdleCallback(idleCallbackId);
1944
+ }
1945
+ if (timeoutId !== null) {
1946
+ clearTimeout(timeoutId);
1947
+ }
1948
+ };
1949
+ }, [contentSignature, shouldDefer]);
1950
+ return renderReady;
1951
+ }
1952
+ function hashMarkdownProfilerContent(content) {
1953
+ let hash = 0;
1954
+ for (let index = 0; index < content.length; index += 1) {
1955
+ hash = hash * 31 + content.charCodeAt(index) | 0;
1956
+ }
1957
+ return `${content.length}:${Math.abs(hash)}`;
1958
+ }
1959
+ function isLikelyLongerThanLineLimit(content) {
1960
+ const normalizedLines = content.replace(/\r\n?/g, "\n").split("\n");
1961
+ if (normalizedLines.length > COLLAPSED_LINE_LIMIT) {
1962
+ return true;
1963
+ }
1964
+ const estimatedLineCount = normalizedLines.reduce((total, line) => {
1965
+ const trimmed = line.trim();
1966
+ const blockSpacing = /^(#{1,6}\s|\s*[-*+]\s|\s*\d+\.\s|>)/.test(trimmed) ? 1 : 0;
1967
+ return total + Math.max(1, Math.ceil(trimmed.length / APPROX_CHARS_PER_LINE)) + blockSpacing;
1968
+ }, 0);
1969
+ return estimatedLineCount > COLLAPSED_LINE_LIMIT;
1970
+ }
1971
+ function MarkdownLink({
1972
+ node: _node,
1973
+ onClick: _onClick,
1974
+ onLinkClick,
1975
+ workspaceAppIcons,
1976
+ href,
1977
+ ...props
1978
+ }) {
1979
+ "use memo";
1980
+ const { t } = useTranslation();
1981
+ const targetHref = href?.trim() ?? "";
1982
+ const mention = targetHref ? parseMentionLink(
1983
+ targetHref,
1984
+ textFromReactNode(props.children),
1985
+ workspaceAppIcons ?? [],
1986
+ t("agentHost.agentGui.workspaceAppFactoryMentionFallback")
1987
+ ) : null;
1988
+ if (mention) {
1989
+ return /* @__PURE__ */ jsx6(
1990
+ MentionLink,
1991
+ {
1992
+ ...props,
1993
+ href: targetHref,
1994
+ mention,
1995
+ onLinkClick
1996
+ }
1997
+ );
1998
+ }
1999
+ const fileMention = targetHref ? parseWorkspaceFileMentionLink(
2000
+ targetHref,
2001
+ textFromReactNode(props.children)
2002
+ ) : null;
2003
+ if (fileMention) {
2004
+ return /* @__PURE__ */ jsx6(
2005
+ WorkspaceFileMentionLink,
2006
+ {
2007
+ ...props,
2008
+ href: targetHref,
2009
+ mention: fileMention,
2010
+ onLinkClick
2011
+ }
2012
+ );
2013
+ }
2014
+ return /* @__PURE__ */ jsx6(MarkdownLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx6(
2015
+ "a",
2016
+ {
2017
+ ...props,
2018
+ "data-agent-link-href": targetHref,
2019
+ role: "link",
2020
+ tabIndex: 0,
2021
+ onClick: (event) => {
2022
+ activateMarkdownLink(event, targetHref, onLinkClick);
2023
+ },
2024
+ onPointerDown: (event) => {
2025
+ activateMarkdownLinkFromPointer(event, targetHref, onLinkClick);
2026
+ },
2027
+ onKeyDown: (event) => {
2028
+ activateMarkdownLinkFromKey(event, targetHref, onLinkClick);
2029
+ }
2030
+ }
2031
+ ) });
2032
+ }
2033
+ function parseWorkspaceFileMentionTarget(href) {
2034
+ const target = href.trim();
2035
+ if (!isLocalAbsolutePath(target)) {
2036
+ return null;
2037
+ }
2038
+ let url;
2039
+ try {
2040
+ url = new URL(target, "https://tsh.local");
2041
+ } catch {
2042
+ return null;
2043
+ }
2044
+ const explicitKindValue = url.searchParams.get("kind") ?? url.searchParams.get("refType") ?? url.searchParams.get("entryKind") ?? "";
2045
+ const normalizedKind = explicitKindValue.trim().toLowerCase();
2046
+ const explicitKind = normalizedKind === "folder" || normalizedKind === "directory" ? "directory" : normalizedKind === "file" ? "file" : null;
2047
+ const path = url.pathname.replace(/\/+$/, "");
2048
+ if (!path || path === "/") {
2049
+ return null;
2050
+ }
2051
+ return {
2052
+ path,
2053
+ explicitKind: explicitKind ?? (url.pathname.endsWith("/") ? "directory" : null)
2054
+ };
2055
+ }
2056
+ function resolveWorkspaceFileMentionEntryKind(path, label, explicitKind) {
2057
+ if (explicitKind) {
2058
+ return explicitKind;
2059
+ }
2060
+ if (resolveWorkspaceFileExtension(path)) {
2061
+ return "file";
2062
+ }
2063
+ const basename = basenameWorkspacePath(path);
2064
+ return basename === label ? "directory" : "file";
2065
+ }
2066
+ function parseWorkspaceFileMentionLink(href, rawLabel) {
2067
+ const label = rawLabel.trim();
2068
+ const target = parseWorkspaceFileMentionTarget(href);
2069
+ if (!target || !label.startsWith("@")) {
2070
+ return null;
2071
+ }
2072
+ const fileLabel = label.replace(/^@+/, "").trim();
2073
+ if (!fileLabel) {
2074
+ return null;
2075
+ }
2076
+ const entryKind = resolveWorkspaceFileMentionEntryKind(
2077
+ target.path,
2078
+ fileLabel,
2079
+ target.explicitKind
2080
+ );
2081
+ return {
2082
+ label: fileLabel,
2083
+ href: target.path,
2084
+ entryKind,
2085
+ visualKind: resolveAgentWorkspaceFileVisualKind(target.path, {
2086
+ refType: entryKind === "directory" ? "folder" : "file"
2087
+ })
2088
+ };
2089
+ }
2090
+ function WorkspaceFileMentionLink({
2091
+ onClick: _onClick,
2092
+ onLinkClick,
2093
+ href: _href,
2094
+ mention,
2095
+ ...props
2096
+ }) {
2097
+ "use memo";
2098
+ return /* @__PURE__ */ jsxs4(
2099
+ "a",
2100
+ {
2101
+ ...props,
2102
+ className: cn(
2103
+ "tsh-workspace-file-link tsh-agent-object-token tsh-agent-object-token--file",
2104
+ props.className
2105
+ ),
2106
+ "data-agent-file-mention": "true",
2107
+ "data-agent-mention-kind": "file",
2108
+ "data-agent-file-entry-kind": mention.entryKind,
2109
+ "data-agent-file-visual-kind": mention.visualKind,
2110
+ "data-agent-link-href": mention.href,
2111
+ "data-agent-mention-href": mention.href,
2112
+ "aria-label": mention.label,
2113
+ role: "link",
2114
+ tabIndex: 0,
2115
+ onClick: (event) => {
2116
+ activateMarkdownLink(event, mention.href, onLinkClick);
2117
+ },
2118
+ onPointerDown: (event) => {
2119
+ activateMarkdownLinkFromPointer(event, mention.href, onLinkClick);
2120
+ },
2121
+ onKeyDown: (event) => {
2122
+ activateMarkdownLinkFromKey(event, mention.href, onLinkClick);
2123
+ },
2124
+ children: [
2125
+ /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__icon", "aria-hidden": "true" }),
2126
+ /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__main", children: mention.label })
2127
+ ]
2128
+ }
2129
+ );
2130
+ }
2131
+ function MentionLink({
2132
+ onClick: _onClick,
2133
+ onLinkClick,
2134
+ href,
2135
+ mention,
2136
+ ...props
2137
+ }) {
2138
+ "use memo";
2139
+ return /* @__PURE__ */ jsxs4(
2140
+ "a",
2141
+ {
2142
+ ...props,
2143
+ className: cn(
2144
+ "tsh-agent-object-token tsh-agent-object-token--entity",
2145
+ props.className
2146
+ ),
2147
+ "data-agent-file-mention": "true",
2148
+ "data-agent-link-href": href,
2149
+ "data-agent-mention-icon-url": mention.iconUrl,
2150
+ "data-agent-mention-href": href,
2151
+ "data-agent-mention-kind": mention.kind,
2152
+ "aria-label": mention.label,
2153
+ role: "link",
2154
+ tabIndex: 0,
2155
+ onClick: (event) => {
2156
+ activateMarkdownLink(event, href, onLinkClick);
2157
+ },
2158
+ onPointerDown: (event) => {
2159
+ activateMarkdownLinkFromPointer(event, href, onLinkClick);
2160
+ },
2161
+ onKeyDown: (event) => {
2162
+ activateMarkdownLinkFromKey(event, href, onLinkClick);
2163
+ },
2164
+ children: [
2165
+ mention.kind === "workspace-app" ? /* @__PURE__ */ jsx6(
2166
+ "span",
2167
+ {
2168
+ className: "grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px] bg-block",
2169
+ "aria-hidden": "true",
2170
+ "data-agent-mention-app-icon": "true",
2171
+ "data-workspace-app-icon": "true",
2172
+ children: mention.iconUrl ? /* @__PURE__ */ jsx6(
2173
+ "img",
2174
+ {
2175
+ src: mention.iconUrl,
2176
+ alt: "",
2177
+ className: "h-full w-full object-cover",
2178
+ decoding: "async",
2179
+ loading: "lazy",
2180
+ draggable: false
2181
+ }
2182
+ ) : /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__kind-icon h-4 w-4" })
2183
+ }
2184
+ ) : /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__kind", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
2185
+ "span",
2186
+ {
2187
+ className: "tsh-agent-object-token__kind-icon",
2188
+ "aria-hidden": "true"
2189
+ }
2190
+ ) }),
2191
+ mention.kind === "session" ? /* @__PURE__ */ jsxs4("span", { className: "tsh-agent-object-token__main", children: [
2192
+ /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__participant", children: mention.participant }),
2193
+ mention.summary ? /* @__PURE__ */ jsxs4("span", { className: "tsh-agent-object-token__summary", children: [
2194
+ " ",
2195
+ mention.summary
2196
+ ] }) : null
2197
+ ] }) : /* @__PURE__ */ jsx6("span", { className: "tsh-agent-object-token__main", children: mention.label })
2198
+ ]
2199
+ }
2200
+ );
2201
+ }
2202
+ function MarkdownCode({
2203
+ node: _node,
2204
+ children,
2205
+ className,
2206
+ onLinkClick,
2207
+ ...props
2208
+ }) {
2209
+ "use memo";
2210
+ const text = textFromReactNode(children).trim();
2211
+ if (!className && onLinkClick && (isLocalAbsolutePath(text) || isHttpUrl(text))) {
2212
+ return /* @__PURE__ */ jsx6("code", { ...props, className, children: /* @__PURE__ */ jsx6(PathLink, { href: text, onLinkClick, children }) });
2213
+ }
2214
+ return /* @__PURE__ */ jsx6("code", { ...props, className, children });
2215
+ }
2216
+ var cachedMarkdownImages = /* @__PURE__ */ new Map();
2217
+ var CACHED_MARKDOWN_IMAGE_REVOKE_DELAY_MS = 250;
2218
+ function MarkdownImage({
2219
+ node: _node,
2220
+ src,
2221
+ alt,
2222
+ className,
2223
+ enableZoom = false,
2224
+ ...props
2225
+ }) {
2226
+ "use memo";
2227
+ const { t } = useTranslation();
2228
+ const isInsideLink = useContext3(MarkdownLinkContext);
2229
+ const agentHostApi = useOptionalAgentHostApi() ?? getOptionalAgentHostApi();
2230
+ const workspacePath = typeof src === "string" && isLocalAbsolutePath(src) ? src.trim() : null;
2231
+ const readWorkspaceImage = workspacePath ? agentHostApi?.workspace?.readFile : void 0;
2232
+ const canReadWorkspaceImage = Boolean(workspacePath && readWorkspaceImage);
2233
+ const shouldEnableZoom = enableZoom && !isInsideLink;
2234
+ const resolvedSrc = typeof src === "string" ? resolveRenderableMarkdownImageSrc(src) : src;
2235
+ const [state, setState] = useState2(
2236
+ () => canReadWorkspaceImage && workspacePath ? peekCachedMarkdownImageState(workspacePath) ?? { status: "loading" } : null
2237
+ );
2238
+ useEffect3(() => {
2239
+ if (!workspacePath || !readWorkspaceImage) {
2240
+ setState(null);
2241
+ return;
2242
+ }
2243
+ const resolvedWorkspacePath = workspacePath;
2244
+ const resolvedReadWorkspaceImage = readWorkspaceImage;
2245
+ const cachedSrc = retainCachedMarkdownImage(resolvedWorkspacePath);
2246
+ if (cachedSrc) {
2247
+ setState({ status: "ready", src: cachedSrc });
2248
+ return () => {
2249
+ releaseCachedMarkdownImage(resolvedWorkspacePath, cachedSrc);
2250
+ };
2251
+ }
2252
+ const resolvedMimeType = resolveWorkspaceImageMimeType(
2253
+ resolvedWorkspacePath
2254
+ );
2255
+ if (!resolvedMimeType) {
2256
+ setState({
2257
+ status: "error",
2258
+ reason: "unsupported"
2259
+ });
2260
+ return;
2261
+ }
2262
+ const imageMimeType = resolvedMimeType;
2263
+ let canceled = false;
2264
+ let objectUrl = null;
2265
+ setState({ status: "loading" });
2266
+ async function loadWorkspaceImage() {
2267
+ try {
2268
+ const result = await resolvedReadWorkspaceImage({
2269
+ path: resolvedWorkspacePath
2270
+ });
2271
+ if (canceled) {
2272
+ return;
2273
+ }
2274
+ const bytes = result.bytes instanceof Uint8Array ? result.bytes : new Uint8Array(result.bytes);
2275
+ const arrayBuffer = bytes.buffer.slice(
2276
+ bytes.byteOffset,
2277
+ bytes.byteOffset + bytes.byteLength
2278
+ );
2279
+ objectUrl = cacheMarkdownImage(
2280
+ resolvedWorkspacePath,
2281
+ new Blob([arrayBuffer], { type: imageMimeType })
2282
+ );
2283
+ setState({ status: "ready", src: objectUrl });
2284
+ } catch (error) {
2285
+ if (!canceled) {
2286
+ setState({
2287
+ status: "error",
2288
+ reason: "read-failed",
2289
+ detail: error instanceof Error ? error.message : String(error)
2290
+ });
2291
+ }
2292
+ }
2293
+ }
2294
+ void loadWorkspaceImage();
2295
+ return () => {
2296
+ canceled = true;
2297
+ if (objectUrl) {
2298
+ releaseCachedMarkdownImage(resolvedWorkspacePath, objectUrl);
2299
+ }
2300
+ };
2301
+ }, [canReadWorkspaceImage, workspacePath]);
2302
+ if (!workspacePath || !readWorkspaceImage) {
2303
+ if (!shouldEnableZoom) {
2304
+ return /* @__PURE__ */ jsx6("img", { ...props, src: resolvedSrc, alt, className });
2305
+ }
2306
+ return /* @__PURE__ */ jsx6(
2307
+ ZoomableImage,
2308
+ {
2309
+ ...props,
2310
+ src: resolvedSrc,
2311
+ alt,
2312
+ className,
2313
+ wrapElement: "span"
2314
+ }
2315
+ );
2316
+ }
2317
+ if (state?.status === "ready") {
2318
+ if (!shouldEnableZoom) {
2319
+ return /* @__PURE__ */ jsx6(
2320
+ "img",
2321
+ {
2322
+ ...props,
2323
+ src: state.src,
2324
+ alt,
2325
+ className: cn(
2326
+ "block max-h-[360px] max-w-full rounded-[8px] border border-[var(--line-2)] bg-[var(--background-panel)] object-contain",
2327
+ className
2328
+ )
2329
+ }
2330
+ );
2331
+ }
2332
+ return /* @__PURE__ */ jsx6(
2333
+ ZoomableImage,
2334
+ {
2335
+ ...props,
2336
+ src: state.src,
2337
+ alt,
2338
+ className: cn(
2339
+ "block max-h-[360px] max-w-full rounded-[8px] border border-[var(--line-2)] bg-[var(--background-panel)] object-contain",
2340
+ className
2341
+ ),
2342
+ wrapElement: "span"
2343
+ }
2344
+ );
2345
+ }
2346
+ return /* @__PURE__ */ jsx6("span", { className: "flex min-h-[160px] w-full items-center justify-center rounded-[8px] border border-[var(--line-2)] bg-[var(--background-panel)] px-5 py-5 text-center text-[13px] leading-5 text-[var(--text-tertiary)]", children: state?.status === "error" ? state.reason === "unsupported" ? t("agentHost.workspaceFileManager.previewUnsupported") : t("agentHost.workspaceFileManager.previewReadFailed", {
2347
+ message: state.detail ?? ""
2348
+ }) : t("agentHost.workspaceFileManager.previewLoading") });
2349
+ }
2350
+ var MARKDOWN_ORDERED_LIST_STYLE = {
2351
+ listStylePosition: "outside",
2352
+ margin: "12px 0 8px",
2353
+ paddingInlineStart: 34,
2354
+ paddingInlineEnd: 16
2355
+ };
2356
+ var MARKDOWN_UNORDERED_LIST_STYLE = {
2357
+ margin: "12px 0 8px",
2358
+ paddingInlineStart: 0
2359
+ };
2360
+ var MARKDOWN_LIST_ITEM_STYLE = {
2361
+ margin: "4px 0"
2362
+ };
2363
+ function MarkdownUnorderedList({
2364
+ node: _node,
2365
+ className,
2366
+ style,
2367
+ ...props
2368
+ }) {
2369
+ "use memo";
2370
+ return /* @__PURE__ */ jsx6(
2371
+ "ul",
2372
+ {
2373
+ ...props,
2374
+ className: cn(
2375
+ '[&_li]:relative [&_li]:list-none [&_li]:pl-[34px] [&_li::before]:absolute [&_li::before]:left-4 [&_li::before]:top-[0.78em] [&_li::before]:h-1.5 [&_li::before]:w-1.5 [&_li::before]:-translate-y-1/2 [&_li::before]:rounded-full [&_li::before]:bg-[var(--text-tertiary)] [&_li::before]:content-[""]',
2376
+ className
2377
+ ),
2378
+ style: { ...MARKDOWN_UNORDERED_LIST_STYLE, ...style }
2379
+ }
2380
+ );
2381
+ }
2382
+ function MarkdownOrderedList({
2383
+ node: _node,
2384
+ style,
2385
+ ...props
2386
+ }) {
2387
+ "use memo";
2388
+ return /* @__PURE__ */ jsx6(
2389
+ "ol",
2390
+ {
2391
+ ...props,
2392
+ style: {
2393
+ ...MARKDOWN_ORDERED_LIST_STYLE,
2394
+ listStyleType: "decimal",
2395
+ ...style
2396
+ }
2397
+ }
2398
+ );
2399
+ }
2400
+ function MarkdownListItem({
2401
+ node: _node,
2402
+ style,
2403
+ ...props
2404
+ }) {
2405
+ "use memo";
2406
+ return /* @__PURE__ */ jsx6("li", { ...props, style: { ...MARKDOWN_LIST_ITEM_STYLE, ...style } });
2407
+ }
2408
+ function MarkdownParagraph({
2409
+ node: _node,
2410
+ inline,
2411
+ ...props
2412
+ }) {
2413
+ "use memo";
2414
+ if (inline) {
2415
+ return /* @__PURE__ */ jsx6("span", { ...props });
2416
+ }
2417
+ return /* @__PURE__ */ jsx6("p", { ...props });
2418
+ }
2419
+ function isLocalAbsolutePath(path) {
2420
+ const candidate = path.trim();
2421
+ return candidate.length > 1 && candidate.startsWith("/") && !candidate.startsWith("//") && !candidate.includes("://") && !/\s/.test(candidate);
2422
+ }
2423
+ function resolveRenderableMarkdownImageSrc(src) {
2424
+ const trimmed = src.trim();
2425
+ if (!trimmed) {
2426
+ return src;
2427
+ }
2428
+ if (!isLocalAbsolutePath(trimmed) || trimmed.startsWith("/workspace/")) {
2429
+ return src;
2430
+ }
2431
+ return new URL(trimmed, "file://").toString();
2432
+ }
2433
+ function peekCachedMarkdownImageState(path) {
2434
+ const src = cachedMarkdownImages.get(path)?.objectUrl ?? null;
2435
+ return src ? { status: "ready", src } : null;
2436
+ }
2437
+ function retainCachedMarkdownImage(path) {
2438
+ const entry = cachedMarkdownImages.get(path);
2439
+ if (!entry) {
2440
+ return null;
2441
+ }
2442
+ entry.refCount += 1;
2443
+ if (entry.revokeTimer) {
2444
+ clearTimeout(entry.revokeTimer);
2445
+ entry.revokeTimer = null;
2446
+ }
2447
+ return entry.objectUrl;
2448
+ }
2449
+ function cacheMarkdownImage(path, blob) {
2450
+ const entry = cachedMarkdownImages.get(path);
2451
+ if (entry) {
2452
+ entry.refCount += 1;
2453
+ if (entry.revokeTimer) {
2454
+ clearTimeout(entry.revokeTimer);
2455
+ entry.revokeTimer = null;
2456
+ }
2457
+ return entry.objectUrl;
2458
+ }
2459
+ const objectUrl = URL.createObjectURL(blob);
2460
+ cachedMarkdownImages.set(path, {
2461
+ objectUrl,
2462
+ refCount: 1,
2463
+ revokeTimer: null
2464
+ });
2465
+ return objectUrl;
2466
+ }
2467
+ function releaseCachedMarkdownImage(path, objectUrl) {
2468
+ const entry = cachedMarkdownImages.get(path);
2469
+ if (!entry || entry.objectUrl !== objectUrl) {
2470
+ URL.revokeObjectURL(objectUrl);
2471
+ return;
2472
+ }
2473
+ entry.refCount = Math.max(0, entry.refCount - 1);
2474
+ if (entry.refCount > 0 || entry.revokeTimer) {
2475
+ return;
2476
+ }
2477
+ entry.revokeTimer = setTimeout(() => {
2478
+ const current = cachedMarkdownImages.get(path);
2479
+ if (!current || current.objectUrl !== objectUrl || current.refCount > 0) {
2480
+ return;
2481
+ }
2482
+ cachedMarkdownImages.delete(path);
2483
+ URL.revokeObjectURL(objectUrl);
2484
+ }, CACHED_MARKDOWN_IMAGE_REVOKE_DELAY_MS);
2485
+ }
2486
+ function isHttpUrl(value) {
2487
+ const candidate = value.trim();
2488
+ if (!candidate || /\s/.test(candidate)) {
2489
+ return false;
2490
+ }
2491
+ try {
2492
+ const url = new URL(candidate);
2493
+ return url.protocol === "http:" || url.protocol === "https:";
2494
+ } catch {
2495
+ return false;
2496
+ }
2497
+ }
2498
+ function linkBareLocalAbsolutePaths(content) {
2499
+ let out = "";
2500
+ for (let index = 0; index < content.length; ) {
2501
+ const markdownLinkEnd = markdownLinkEndIndex(content, index);
2502
+ if (markdownLinkEnd > index) {
2503
+ out += content.slice(index, markdownLinkEnd);
2504
+ index = markdownLinkEnd;
2505
+ continue;
2506
+ }
2507
+ const codeSpanEnd = codeSpanEndIndex(content, index);
2508
+ if (codeSpanEnd > index) {
2509
+ out += content.slice(index, codeSpanEnd);
2510
+ index = codeSpanEnd;
2511
+ continue;
2512
+ }
2513
+ if (isLocalPathStart(content, index)) {
2514
+ const end = bareLocalPathEndIndex(content, index);
2515
+ const rawPath = trimTrailingPathPunctuation(content.slice(index, end));
2516
+ const trailing = content.slice(index + rawPath.length, end);
2517
+ if (isLocalAbsolutePath(rawPath)) {
2518
+ out += `[${escapeMarkdownLinkLabel(rawPath)}](${rawPath})${trailing}`;
2519
+ } else {
2520
+ out += content.slice(index, end);
2521
+ }
2522
+ index = end;
2523
+ continue;
2524
+ }
2525
+ out += content[index];
2526
+ index += 1;
2527
+ }
2528
+ return out;
2529
+ }
2530
+ function normalizePlainIssueMentionTitleContent(content) {
2531
+ const trimmed = content.trim();
2532
+ if (trimmed !== content || !trimmed.startsWith("@") || trimmed.includes("\n") || markdownLinkEndIndex(trimmed, 0) === trimmed.length) {
2533
+ return content;
2534
+ }
2535
+ const label = trimmed.replace(/^@+/, "").trim();
2536
+ if (!label) {
2537
+ return content;
2538
+ }
2539
+ return `[@${escapeMarkdownLinkLabel(label)}](mention://workspace-issue?source=plain-title)`;
2540
+ }
2541
+ function normalizeMentionMarkdownLinks(content) {
2542
+ return content.replace(/\]([\t ]*\r?\n[\t ]*)+\((mention:\/\/)/g, "]($2").replace(/\]\((mention:\/\/[A-Za-z0-9.-]+)\)\?([^\s)]+)/g, "]($1?$2)");
2543
+ }
2544
+ function isMentionOnlyMarkdownContent(content) {
2545
+ const trimmed = content.trim();
2546
+ if (trimmed.length === 0) {
2547
+ return false;
2548
+ }
2549
+ if (markdownLinkEndIndex(trimmed, 0) !== trimmed.length) {
2550
+ return false;
2551
+ }
2552
+ const labelEnd = trimmed.indexOf("]");
2553
+ return trimmed.slice(labelEnd + 2).startsWith("mention://");
2554
+ }
2555
+ function normalizePlainSessionMentionTitle(content) {
2556
+ const trimmed = content.trim();
2557
+ if (trimmed !== content || !trimmed.startsWith("@") || trimmed.includes("\n")) {
2558
+ return content;
2559
+ }
2560
+ for (const agentLabel of PLAIN_SESSION_MENTION_AGENT_LABELS) {
2561
+ const separator = ` & ${agentLabel}`;
2562
+ const separatorIndex = trimmed.indexOf(separator);
2563
+ if (separatorIndex <= 1) {
2564
+ continue;
2565
+ }
2566
+ const userLabel = trimmed.slice(1, separatorIndex).trim();
2567
+ const summary = trimmed.slice(separatorIndex + separator.length).trim();
2568
+ if (!userLabel) {
2569
+ continue;
2570
+ }
2571
+ const mentionLabel = [userLabel, agentLabel, summary].filter(Boolean).join(" \xB7 ");
2572
+ return `[@${escapeMarkdownLinkLabel(mentionLabel)}](mention://agent-session?source=plain-title)`;
2573
+ }
2574
+ return content;
2575
+ }
2576
+ function markdownUrlTransform(value) {
2577
+ return value.startsWith("mention://") ? value : defaultUrlTransform(value);
2578
+ }
2579
+ function parseMentionLink(href, rawLabel, workspaceAppIcons = [], appFactoryFallbackLabel = "Create app") {
2580
+ let url;
2581
+ try {
2582
+ url = new URL(href);
2583
+ } catch {
2584
+ return null;
2585
+ }
2586
+ if (url.protocol !== "mention:") {
2587
+ return null;
2588
+ }
2589
+ const resource = url.hostname.trim().toLowerCase();
2590
+ const kind = resource === "agent-session" ? "session" : resource === "workspace-app" ? "workspace-app" : resource === "workspace-app-factory" ? "workspace-app-factory" : resource === "workspace-issue" ? "workspace-issue" : resource;
2591
+ if (kind !== "session" && kind !== "workspace-app" && kind !== "workspace-app-factory" && kind !== "workspace-issue") {
2592
+ return null;
2593
+ }
2594
+ const label = rawLabel.trim().replace(/^@+/, "").trim() || (kind === "workspace-app-factory" ? appFactoryFallbackLabel : "");
2595
+ if (kind === "workspace-app" || kind === "workspace-app-factory") {
2596
+ const appId = url.searchParams.get("appId")?.trim() || "";
2597
+ const workspaceId = url.searchParams.get("workspaceId")?.trim() || "";
2598
+ return {
2599
+ kind,
2600
+ ...kind === "workspace-app" ? { appId } : {},
2601
+ label,
2602
+ ...kind === "workspace-app" ? {
2603
+ iconUrl: resolveWorkspaceAppMentionIconUrl({
2604
+ appId,
2605
+ workspaceAppIcons,
2606
+ workspaceId
2607
+ })
2608
+ } : {},
2609
+ participant: label,
2610
+ summary: ""
2611
+ };
2612
+ }
2613
+ if (kind === "workspace-issue") {
2614
+ return {
2615
+ kind,
2616
+ label,
2617
+ participant: label,
2618
+ summary: ""
2619
+ };
2620
+ }
2621
+ const sessionLabel = parseSessionMentionLabel(label);
2622
+ return {
2623
+ kind,
2624
+ label,
2625
+ participant: sessionLabel.participant,
2626
+ summary: sessionLabel.summary
2627
+ };
2628
+ }
2629
+ function resolveWorkspaceAppMentionIconUrl(input) {
2630
+ const appId = input.appId.trim();
2631
+ if (!appId) {
2632
+ return void 0;
2633
+ }
2634
+ const workspaceId = input.workspaceId.trim();
2635
+ const exactMatch = input.workspaceAppIcons.find(
2636
+ (icon) => icon.appId.trim() === appId && (icon.workspaceId?.trim() ?? "") === workspaceId && icon.iconUrl?.trim()
2637
+ );
2638
+ const fallbackMatch = input.workspaceAppIcons.find(
2639
+ (icon) => icon.appId.trim() === appId && icon.iconUrl?.trim()
2640
+ );
2641
+ return exactMatch?.iconUrl?.trim() || fallbackMatch?.iconUrl?.trim() || void 0;
2642
+ }
2643
+ function parseSessionMentionLabel(label) {
2644
+ const dottedParts = label.split("\xB7").map((part) => part.trim()).filter(Boolean);
2645
+ if (dottedParts.length >= 3) {
2646
+ return {
2647
+ participant: `${dottedParts[0]} & ${dottedParts[1]}`,
2648
+ summary: dottedParts.slice(2).join(" ")
2649
+ };
2650
+ }
2651
+ return {
2652
+ participant: label,
2653
+ summary: ""
2654
+ };
2655
+ }
2656
+ function markdownLinkEndIndex(content, index) {
2657
+ if (content[index] !== "[") {
2658
+ return -1;
2659
+ }
2660
+ const labelEnd = content.indexOf("]", index + 1);
2661
+ if (labelEnd < 0 || content[labelEnd + 1] !== "(") {
2662
+ return -1;
2663
+ }
2664
+ const hrefEnd = content.indexOf(")", labelEnd + 2);
2665
+ return hrefEnd < 0 ? -1 : hrefEnd + 1;
2666
+ }
2667
+ function codeSpanEndIndex(content, index) {
2668
+ if (content[index] !== "`") {
2669
+ return -1;
2670
+ }
2671
+ let tickCount = 1;
2672
+ while (content[index + tickCount] === "`") {
2673
+ tickCount += 1;
2674
+ }
2675
+ const fence = "`".repeat(tickCount);
2676
+ const end = content.indexOf(fence, index + tickCount);
2677
+ return end < 0 ? -1 : end + tickCount;
2678
+ }
2679
+ function isLocalPathStart(content, index) {
2680
+ if (content[index] !== "/" || content[index + 1] === "/") {
2681
+ return false;
2682
+ }
2683
+ const previous = content[index - 1];
2684
+ return previous === void 0 || /\s/.test(previous) || previous === "(" || previous === "[";
2685
+ }
2686
+ function bareLocalPathEndIndex(content, index) {
2687
+ let end = index;
2688
+ while (end < content.length) {
2689
+ const char = content[end];
2690
+ if (!char || /[\s<>[\](){}"'`]/.test(char)) {
2691
+ break;
2692
+ }
2693
+ end += 1;
2694
+ }
2695
+ return end;
2696
+ }
2697
+ function trimTrailingPathPunctuation(path) {
2698
+ return path.replace(/[.,;:!?,。;:!?]+$/g, "");
2699
+ }
2700
+ function escapeMarkdownLinkLabel(label) {
2701
+ return label.replace(/([\\[\]])/g, "\\$1");
2702
+ }
2703
+ function PathLink({
2704
+ href,
2705
+ children,
2706
+ onLinkClick
2707
+ }) {
2708
+ "use memo";
2709
+ return /* @__PURE__ */ jsx6(
2710
+ "a",
2711
+ {
2712
+ className: "cursor-pointer",
2713
+ "data-agent-link-href": href,
2714
+ role: "link",
2715
+ tabIndex: 0,
2716
+ onClick: (event) => {
2717
+ activateMarkdownLink(event, href, onLinkClick);
2718
+ },
2719
+ onPointerDown: (event) => {
2720
+ activateMarkdownLinkFromPointer(event, href, onLinkClick);
2721
+ },
2722
+ onKeyDown: (event) => {
2723
+ activateMarkdownLinkFromKey(event, href, onLinkClick);
2724
+ },
2725
+ children
2726
+ }
2727
+ );
2728
+ }
2729
+ function textFromReactNode(node) {
2730
+ if (typeof node === "string" || typeof node === "number") {
2731
+ return String(node);
2732
+ }
2733
+ if (Array.isArray(node)) {
2734
+ return node.map(textFromReactNode).join("");
2735
+ }
2736
+ return "";
2737
+ }
2738
+
2739
+ // app/renderer/assets/icons/user-avatar-placeholder.png
2740
+ var user_avatar_placeholder_default = "./user-avatar-placeholder-WP2373TS.png";
2741
+
2742
+ // shared/workspaceAgentActivityStatusLabel.ts
2743
+ function normalizeWorkspaceAgentActivityDisplayStatus(status) {
2744
+ switch ((status ?? "").trim().toLowerCase()) {
2745
+ case "working":
2746
+ return "working";
2747
+ case "waiting":
2748
+ return "waiting";
2749
+ case "idle":
2750
+ case "ready":
2751
+ return "completed";
2752
+ case "completed":
2753
+ case "end":
2754
+ return "completed";
2755
+ case "canceled":
2756
+ return "canceled";
2757
+ case "failed":
2758
+ return "failed";
2759
+ default:
2760
+ return "idle";
2761
+ }
2762
+ }
2763
+ function workspaceAgentActivityStatusLabel(status, t) {
2764
+ const translateFn = t ?? translate;
2765
+ switch (normalizeWorkspaceAgentActivityDisplayStatus(status)) {
2766
+ case "working":
2767
+ return translateFn("agentHost.workspaceAgentActivityStatusWorking");
2768
+ case "waiting":
2769
+ return translateFn("agentHost.workspaceAgentActivityStatusWaiting");
2770
+ case "idle":
2771
+ return translateFn("agentHost.workspaceAgentActivityStatusIdle");
2772
+ case "completed":
2773
+ return translateFn("agentHost.workspaceAgentActivityStatusEnd");
2774
+ case "canceled":
2775
+ return translateFn("agentHost.workspaceAgentActivityStatusCanceled");
2776
+ case "failed":
2777
+ return translateFn("agentHost.workspaceAgentActivityStatusFailed");
2778
+ default:
2779
+ return String(status);
2780
+ }
2781
+ }
2782
+
2783
+ // app/renderer/components/ui/custom-scroll-area.tsx
2784
+ import {
2785
+ forwardRef as forwardRef2,
2786
+ useCallback as useCallback4,
2787
+ useEffect as useEffect4,
2788
+ useRef as useRef3,
2789
+ useState as useState3
2790
+ } from "react";
2791
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2792
+ var MIN_THUMB_HEIGHT = 24;
2793
+ function CustomScrollbar({
2794
+ getViewport,
2795
+ className,
2796
+ thumbClassName,
2797
+ testId,
2798
+ thumbTestId,
2799
+ syncKey
2800
+ }) {
2801
+ "use memo";
2802
+ const trackRef = useRef3(null);
2803
+ const dragStateRef = useRef3(null);
2804
+ const [scrollbarState, setScrollbarState] = useState3({
2805
+ scrollable: false,
2806
+ thumbHeight: 0,
2807
+ thumbTop: 0
2808
+ });
2809
+ const [dragging, setDragging] = useState3(false);
2810
+ const syncScrollbarState = useCallback4(() => {
2811
+ const viewport = getViewport();
2812
+ if (!viewport) {
2813
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2814
+ return;
2815
+ }
2816
+ const { clientHeight, scrollHeight, scrollTop } = viewport;
2817
+ const trackHeight = trackRef.current?.clientHeight ?? clientHeight;
2818
+ const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
2819
+ if (clientHeight <= 0 || trackHeight <= 0 || maxScrollTop <= 0) {
2820
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2821
+ return;
2822
+ }
2823
+ const thumbHeight = Math.max(
2824
+ MIN_THUMB_HEIGHT,
2825
+ Math.round(clientHeight / scrollHeight * trackHeight)
2826
+ );
2827
+ const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
2828
+ const thumbTop = Math.round(scrollTop / maxScrollTop * maxThumbTop);
2829
+ setScrollbarState(
2830
+ (previous) => previous.scrollable && previous.thumbHeight === thumbHeight && previous.thumbTop === thumbTop ? previous : { scrollable: true, thumbHeight, thumbTop }
2831
+ );
2832
+ }, [getViewport]);
2833
+ const scrollViewportToThumbTop = useCallback4(
2834
+ (thumbTop) => {
2835
+ const viewport = getViewport();
2836
+ const track = trackRef.current;
2837
+ if (!viewport || !track) {
2838
+ return;
2839
+ }
2840
+ const maxScrollTop = Math.max(
2841
+ 0,
2842
+ viewport.scrollHeight - viewport.clientHeight
2843
+ );
2844
+ const maxThumbTop = Math.max(
2845
+ 0,
2846
+ track.clientHeight - scrollbarState.thumbHeight
2847
+ );
2848
+ if (maxScrollTop <= 0 || maxThumbTop <= 0) {
2849
+ return;
2850
+ }
2851
+ viewport.scrollTop = clamp(thumbTop, 0, maxThumbTop) / maxThumbTop * maxScrollTop;
2852
+ syncScrollbarState();
2853
+ },
2854
+ [getViewport, scrollbarState.thumbHeight, syncScrollbarState]
2855
+ );
2856
+ const handleTrackMouseDown = useCallback4(
2857
+ (event) => {
2858
+ if (event.button !== 0 || !scrollbarState.scrollable) {
2859
+ return;
2860
+ }
2861
+ const track = trackRef.current;
2862
+ if (!track) {
2863
+ return;
2864
+ }
2865
+ event.preventDefault();
2866
+ event.stopPropagation();
2867
+ const trackRect = track.getBoundingClientRect();
2868
+ scrollViewportToThumbTop(
2869
+ event.clientY - trackRect.top - scrollbarState.thumbHeight / 2
2870
+ );
2871
+ },
2872
+ [
2873
+ scrollViewportToThumbTop,
2874
+ scrollbarState.scrollable,
2875
+ scrollbarState.thumbHeight
2876
+ ]
2877
+ );
2878
+ const handleThumbMouseDown = useCallback4(
2879
+ (event) => {
2880
+ if (event.button !== 0 || !scrollbarState.scrollable) {
2881
+ return;
2882
+ }
2883
+ const viewport = getViewport();
2884
+ const track = trackRef.current;
2885
+ if (!viewport || !track) {
2886
+ return;
2887
+ }
2888
+ const maxScrollTop = Math.max(
2889
+ 0,
2890
+ viewport.scrollHeight - viewport.clientHeight
2891
+ );
2892
+ const maxThumbTop = Math.max(
2893
+ 0,
2894
+ track.clientHeight - scrollbarState.thumbHeight
2895
+ );
2896
+ if (maxScrollTop <= 0 || maxThumbTop <= 0) {
2897
+ return;
2898
+ }
2899
+ event.preventDefault();
2900
+ event.stopPropagation();
2901
+ dragStateRef.current = {
2902
+ maxScrollTop,
2903
+ maxThumbTop,
2904
+ startClientY: event.clientY,
2905
+ startScrollTop: viewport.scrollTop
2906
+ };
2907
+ setDragging(true);
2908
+ },
2909
+ [getViewport, scrollbarState.scrollable, scrollbarState.thumbHeight]
2910
+ );
2911
+ useEffect4(() => {
2912
+ if (!dragging) {
2913
+ return;
2914
+ }
2915
+ const handleMouseMove = (event) => {
2916
+ const dragState = dragStateRef.current;
2917
+ const viewport = getViewport();
2918
+ if (!dragState || !viewport) {
2919
+ return;
2920
+ }
2921
+ const nextThumbTop = dragState.startScrollTop / dragState.maxScrollTop * dragState.maxThumbTop + (event.clientY - dragState.startClientY);
2922
+ viewport.scrollTop = clamp(nextThumbTop, 0, dragState.maxThumbTop) / dragState.maxThumbTop * dragState.maxScrollTop;
2923
+ syncScrollbarState();
2924
+ };
2925
+ const handleMouseUp = () => {
2926
+ dragStateRef.current = null;
2927
+ setDragging(false);
2928
+ };
2929
+ window.addEventListener("mousemove", handleMouseMove);
2930
+ window.addEventListener("mouseup", handleMouseUp);
2931
+ return () => {
2932
+ window.removeEventListener("mousemove", handleMouseMove);
2933
+ window.removeEventListener("mouseup", handleMouseUp);
2934
+ };
2935
+ }, [dragging, getViewport, syncScrollbarState]);
2936
+ useEffect4(() => {
2937
+ const viewport = getViewport();
2938
+ if (!viewport) {
2939
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2940
+ return;
2941
+ }
2942
+ syncScrollbarState();
2943
+ viewport.addEventListener("scroll", syncScrollbarState, { passive: true });
2944
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(syncScrollbarState) : null;
2945
+ resizeObserver?.observe(viewport);
2946
+ const animationFrameId = window.requestAnimationFrame(syncScrollbarState);
2947
+ return () => {
2948
+ window.cancelAnimationFrame(animationFrameId);
2949
+ viewport.removeEventListener("scroll", syncScrollbarState);
2950
+ resizeObserver?.disconnect();
2951
+ };
2952
+ }, [getViewport, syncKey, syncScrollbarState]);
2953
+ return /* @__PURE__ */ jsx7(
2954
+ "div",
2955
+ {
2956
+ ref: trackRef,
2957
+ className: cn("tsh-custom-scrollbar", className),
2958
+ "data-scrollable": scrollbarState.scrollable ? "true" : "false",
2959
+ "data-dragging": dragging ? "true" : "false",
2960
+ "data-testid": testId,
2961
+ "aria-hidden": "true",
2962
+ onMouseDown: handleTrackMouseDown,
2963
+ children: /* @__PURE__ */ jsx7(
2964
+ "div",
2965
+ {
2966
+ className: cn("tsh-custom-scrollbar__thumb", thumbClassName),
2967
+ "data-testid": thumbTestId,
2968
+ onMouseDown: handleThumbMouseDown,
2969
+ style: {
2970
+ height: `${scrollbarState.thumbHeight}px`,
2971
+ transform: `translateY(${scrollbarState.thumbTop}px)`
2972
+ }
2973
+ }
2974
+ )
2975
+ }
2976
+ );
2977
+ }
2978
+ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2979
+ children,
2980
+ className,
2981
+ viewportClassName,
2982
+ scrollbarClassName,
2983
+ scrollbarThumbClassName,
2984
+ scrollbarTestId,
2985
+ scrollbarThumbTestId,
2986
+ syncKey,
2987
+ ...viewportProps
2988
+ }, forwardedRef) {
2989
+ "use memo";
2990
+ const viewportRef = useRef3(null);
2991
+ const getViewport = useCallback4(() => viewportRef.current, []);
2992
+ return /* @__PURE__ */ jsxs5(
2993
+ "div",
2994
+ {
2995
+ className: cn(
2996
+ "tsh-custom-scroll-area relative min-h-0 min-w-0",
2997
+ className
2998
+ ),
2999
+ children: [
3000
+ /* @__PURE__ */ jsx7(
3001
+ "div",
3002
+ {
3003
+ ref: setRefs(viewportRef, forwardedRef),
3004
+ className: cn(
3005
+ "overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
3006
+ viewportClassName
3007
+ ),
3008
+ ...viewportProps,
3009
+ children
3010
+ }
3011
+ ),
3012
+ /* @__PURE__ */ jsx7(
3013
+ CustomScrollbar,
3014
+ {
3015
+ getViewport,
3016
+ className: scrollbarClassName,
3017
+ thumbClassName: scrollbarThumbClassName,
3018
+ testId: scrollbarTestId,
3019
+ thumbTestId: scrollbarThumbTestId,
3020
+ syncKey: syncKey ?? children
3021
+ }
3022
+ )
3023
+ ]
3024
+ }
3025
+ );
3026
+ });
3027
+ function setRefs(localRef, forwardedRef) {
3028
+ return (node) => {
3029
+ localRef.current = node;
3030
+ if (typeof forwardedRef === "function") {
3031
+ forwardedRef(node);
3032
+ } else if (forwardedRef) {
3033
+ forwardedRef.current = node;
3034
+ }
3035
+ };
3036
+ }
3037
+ function clamp(value, min, max) {
3038
+ return Math.min(max, Math.max(min, value));
3039
+ }
3040
+
3041
+ export {
3042
+ cn,
3043
+ AgentActivityRuntimeProvider,
3044
+ useAgentActivityRuntime,
3045
+ useOptionalAgentActivityRuntime,
3046
+ useAgentActivitySnapshot,
3047
+ getAgentActivityRuntime,
3048
+ getOptionalAgentActivityRuntime,
3049
+ resetAgentActivityRuntimeForTests,
3050
+ setAgentActivityRuntimeForTests,
3051
+ AgentActivityHostProvider,
3052
+ useAgentHostApi,
3053
+ useOptionalAgentHostApi,
3054
+ getOptionalAgentHostApi,
3055
+ resolveWebsiteNavigationUrl,
3056
+ ZoomableImage,
3057
+ resolveWorkspaceLinkAction,
3058
+ AgentMessageMarkdown,
3059
+ AgentGUIConversation_styles_default,
3060
+ CustomScrollArea,
3061
+ MessageSquareMoreIcon,
3062
+ normalizeManagedAgentProvider,
3063
+ MANAGED_AGENT_ICON_URLS,
3064
+ MANAGED_AGENT_ICON_FALLBACK_URL,
3065
+ managedAgentRoundedIconUrl,
3066
+ approvalOptionDisplayLabel,
3067
+ Spinner,
3068
+ getPromptToolDetails,
3069
+ isPromptRequestIdTitle,
3070
+ AgentInteractivePromptSurface,
3071
+ user_avatar_placeholder_default,
3072
+ normalizeWorkspaceAgentActivityDisplayStatus,
3073
+ workspaceAgentActivityStatusLabel
3074
+ };
3075
+ //# sourceMappingURL=chunk-AF5CXBJN.js.map