@tutti-os/agent-gui 0.0.9 → 0.0.11

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 (48) hide show
  1. package/dist/AgentMessageMarkdown-DeYPURtF.d.ts +7 -0
  2. package/dist/agent-conversation/index.d.ts +135 -0
  3. package/dist/agent-conversation/index.js +77 -0
  4. package/dist/agent-conversation/index.js.map +1 -0
  5. package/dist/agent-message-center/index.d.ts +45 -46
  6. package/dist/agent-message-center/index.js +1799 -1166
  7. package/dist/agent-message-center/index.js.map +1 -1
  8. package/dist/agent-rich-text-at-provider.d.ts +15 -1
  9. package/dist/agent-rich-text-at-provider.js +1 -1
  10. package/dist/agentConversationVM-BtHYOTgv.d.ts +342 -0
  11. package/dist/app/renderer/agentactivity.css +208 -0
  12. package/dist/{chunk-KCC3GNPB.js → chunk-77HBKGHF.js} +1 -1
  13. package/dist/chunk-77HBKGHF.js.map +1 -0
  14. package/dist/{chunk-T6U7AB7F.js → chunk-B7K776UD.js} +47 -3
  15. package/dist/chunk-B7K776UD.js.map +1 -0
  16. package/dist/{chunk-CUQJ3VO3.js → chunk-CGBRAWTD.js} +282 -2531
  17. package/dist/chunk-CGBRAWTD.js.map +1 -0
  18. package/dist/{chunk-3D5VTIKP.js → chunk-IBIMGLCD.js} +1 -28
  19. package/dist/chunk-IBIMGLCD.js.map +1 -0
  20. package/dist/{chunk-EHLA6MSQ.js → chunk-ITMKZRCT.js} +9 -19
  21. package/dist/chunk-ITMKZRCT.js.map +1 -0
  22. package/dist/chunk-MKHDRIGN.js +71 -0
  23. package/dist/chunk-MKHDRIGN.js.map +1 -0
  24. package/dist/chunk-QTZALZIV.js +2640 -0
  25. package/dist/chunk-QTZALZIV.js.map +1 -0
  26. package/dist/chunk-TYGL25EL.js +30 -0
  27. package/dist/chunk-TYGL25EL.js.map +1 -0
  28. package/dist/chunk-XJ34OIEQ.js +18 -0
  29. package/dist/chunk-XJ34OIEQ.js.map +1 -0
  30. package/dist/chunk-ZEFETOTS.js +13293 -0
  31. package/dist/chunk-ZEFETOTS.js.map +1 -0
  32. package/dist/i18n/index.d.ts +46 -2
  33. package/dist/i18n/index.js +1 -1
  34. package/dist/index.d.ts +9 -8
  35. package/dist/index.js +9713 -21513
  36. package/dist/index.js.map +1 -1
  37. package/dist/plan-decision-ops.d.ts +70 -0
  38. package/dist/plan-decision-ops.js +19 -0
  39. package/dist/plan-decision-ops.js.map +1 -0
  40. package/dist/workspace-agent-generated-files.d.ts +1 -1
  41. package/dist/workspace-agent-generated-files.js +5 -3
  42. package/dist/{workspaceAgentActivityListViewModel-Be4zm3nk.d.ts → workspaceAgentActivityListViewModel-BLLYGuFO.d.ts} +36 -2
  43. package/package.json +24 -10
  44. package/dist/chunk-3D5VTIKP.js.map +0 -1
  45. package/dist/chunk-CUQJ3VO3.js.map +0 -1
  46. package/dist/chunk-EHLA6MSQ.js.map +0 -1
  47. package/dist/chunk-KCC3GNPB.js.map +0 -1
  48. package/dist/chunk-T6U7AB7F.js.map +0 -1
@@ -0,0 +1,2640 @@
1
+ import {
2
+ useTranslation
3
+ } from "./chunk-B7K776UD.js";
4
+ import {
5
+ resolveAgentWorkspaceFileVisualKind
6
+ } from "./chunk-PJP5BUU6.js";
7
+
8
+ // agentActivityRuntime.tsx
9
+ import {
10
+ createContext,
11
+ useContext,
12
+ useSyncExternalStore
13
+ } from "react";
14
+ import { jsx } from "react/jsx-runtime";
15
+ var AgentActivityRuntimeContext = createContext(
16
+ null
17
+ );
18
+ var currentAgentActivityRuntime = null;
19
+ function AgentActivityRuntimeProvider({
20
+ children,
21
+ runtime
22
+ }) {
23
+ currentAgentActivityRuntime = runtime ?? null;
24
+ return /* @__PURE__ */ jsx(AgentActivityRuntimeContext.Provider, { value: runtime ?? null, children });
25
+ }
26
+ function useAgentActivityRuntime() {
27
+ const runtime = useContext(AgentActivityRuntimeContext) ?? getTestAgentActivityRuntime();
28
+ if (!runtime) {
29
+ throw new Error(
30
+ "AgentActivityRuntimeProvider is missing an AgentActivityRuntime instance."
31
+ );
32
+ }
33
+ return runtime;
34
+ }
35
+ function useOptionalAgentActivityRuntime() {
36
+ return useContext(AgentActivityRuntimeContext) ?? getTestAgentActivityRuntime();
37
+ }
38
+ function useAgentActivitySnapshot(workspaceId) {
39
+ const runtime = useAgentActivityRuntime();
40
+ const normalizedWorkspaceId = workspaceId.trim();
41
+ return useSyncExternalStore(
42
+ (listener) => runtime.subscribe(normalizedWorkspaceId, listener),
43
+ () => runtime.getSnapshot(normalizedWorkspaceId),
44
+ () => runtime.getSnapshot(normalizedWorkspaceId)
45
+ );
46
+ }
47
+ function getAgentActivityRuntime() {
48
+ const runtime = getExplicitWindowTestAgentActivityRuntime() ?? currentAgentActivityRuntime ?? getTestAgentActivityRuntime();
49
+ if (!runtime) {
50
+ throw new Error(
51
+ "AgentActivityRuntimeProvider is missing an AgentActivityRuntime instance."
52
+ );
53
+ }
54
+ return runtime;
55
+ }
56
+ function getOptionalAgentActivityRuntime() {
57
+ return getExplicitWindowTestAgentActivityRuntime() ?? currentAgentActivityRuntime ?? getTestAgentActivityRuntime();
58
+ }
59
+ function resetAgentActivityRuntimeForTests() {
60
+ if (process.env.NODE_ENV === "test") {
61
+ currentAgentActivityRuntime = null;
62
+ }
63
+ }
64
+ function setAgentActivityRuntimeForTests(runtime) {
65
+ if (process.env.NODE_ENV === "test") {
66
+ currentAgentActivityRuntime = runtime;
67
+ }
68
+ }
69
+ function getTestAgentActivityRuntime() {
70
+ if (process.env.NODE_ENV !== "test") {
71
+ return null;
72
+ }
73
+ if (typeof window === "undefined") {
74
+ return null;
75
+ }
76
+ const explicitRuntime = getExplicitWindowTestAgentActivityRuntime();
77
+ if (explicitRuntime) {
78
+ return explicitRuntime;
79
+ }
80
+ if (currentAgentActivityRuntime) {
81
+ return currentAgentActivityRuntime;
82
+ }
83
+ const testRuntime = window.agentActivityRuntime;
84
+ return testRuntime ?? null;
85
+ }
86
+ function getExplicitWindowTestAgentActivityRuntime() {
87
+ if (process.env.NODE_ENV !== "test" || typeof window === "undefined") {
88
+ return null;
89
+ }
90
+ const testDescriptor = Object.getOwnPropertyDescriptor(
91
+ window,
92
+ "agentActivityRuntime"
93
+ );
94
+ if (!testDescriptor || !("value" in testDescriptor)) {
95
+ return null;
96
+ }
97
+ return testDescriptor.value ?? null;
98
+ }
99
+
100
+ // agentActivityHost.tsx
101
+ import {
102
+ createContext as createContext2,
103
+ useContext as useContext2,
104
+ useMemo
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 = useMemo(
136
+ () => agentHostApi ? toAgentHostRuntimeApi(agentHostApi) : null,
137
+ [agentHostApi]
138
+ );
139
+ currentAgentHostApi = resolvedAgentHostApi;
140
+ return /* @__PURE__ */ jsx2(AgentActivityRuntimeProvider, { runtime: agentActivityRuntime, children: /* @__PURE__ */ jsx2(AgentActivityHostContext.Provider, { value: resolvedAgentHostApi, children }) });
141
+ }
142
+ function useAgentHostApi() {
143
+ const agentHostApi = useContext2(AgentActivityHostContext) ?? getTestAgentHostApi();
144
+ if (!agentHostApi) {
145
+ throw new Error(
146
+ "AgentActivityHostProvider is missing an agentHostApi instance."
147
+ );
148
+ }
149
+ return agentHostApi;
150
+ }
151
+ function useOptionalAgentHostApi() {
152
+ return useContext2(AgentActivityHostContext) ?? getTestAgentHostApi();
153
+ }
154
+ function getOptionalAgentHostApi() {
155
+ return getExplicitWindowTestAgentHostApi() ?? currentAgentHostApi ?? getTestAgentHostApi();
156
+ }
157
+ function getTestAgentHostApi() {
158
+ if (process.env.NODE_ENV !== "test") {
159
+ return null;
160
+ }
161
+ if (typeof window === "undefined") {
162
+ return null;
163
+ }
164
+ const explicitAgentHostApi = getExplicitWindowTestAgentHostApi();
165
+ if (explicitAgentHostApi) {
166
+ return explicitAgentHostApi;
167
+ }
168
+ if (currentAgentHostApi) {
169
+ return currentAgentHostApi;
170
+ }
171
+ const testAgentHostApi = window.agentHostApi;
172
+ return testAgentHostApi ? toAgentHostRuntimeApi(testAgentHostApi) : null;
173
+ }
174
+ function getExplicitWindowTestAgentHostApi() {
175
+ if (process.env.NODE_ENV !== "test" || typeof window === "undefined") {
176
+ return null;
177
+ }
178
+ const testDescriptor = Object.getOwnPropertyDescriptor(
179
+ window,
180
+ "agentHostApi"
181
+ );
182
+ if (!testDescriptor || !("value" in testDescriptor)) {
183
+ return null;
184
+ }
185
+ const testAgentHostApi = testDescriptor.value;
186
+ return testAgentHostApi ? toAgentHostRuntimeApi(testAgentHostApi) : null;
187
+ }
188
+
189
+ // shared/agentMcpToolTarget.ts
190
+ function extractAgentMcpToolTarget({
191
+ input: rawInput,
192
+ metadata: rawMetadata,
193
+ payload: rawPayload,
194
+ toolName
195
+ }) {
196
+ const payload = objectValue(rawPayload);
197
+ const input = objectValue(rawInput) ?? objectValue(payload?.input);
198
+ const metadata = objectValue(rawMetadata) ?? objectValue(payload?.metadata);
199
+ const toolCall = objectValue(input?.toolCall);
200
+ const toolCallRawInput = objectValue(toolCall?.rawInput);
201
+ const request = objectValue(input?.request) ?? objectValue(toolCall?.request);
202
+ const requestMeta = objectValue(request?._meta);
203
+ const parsedToolName = parseMcpToolName(
204
+ firstString(toolName, stringValue(metadata?.toolName))
205
+ );
206
+ const server = firstString(
207
+ stringValue(metadata?.server),
208
+ stringValue(metadata?.serverName),
209
+ stringValue(metadata?.mcpServer),
210
+ stringValue(input?.server),
211
+ stringValue(input?.server_name),
212
+ stringValue(input?.serverName),
213
+ stringValue(input?.mcpServer),
214
+ stringValue(toolCall?.server),
215
+ stringValue(toolCall?.server_name),
216
+ stringValue(toolCall?.serverName),
217
+ stringValue(toolCallRawInput?.server),
218
+ stringValue(toolCallRawInput?.server_name),
219
+ stringValue(toolCallRawInput?.serverName),
220
+ parsedToolName?.server
221
+ );
222
+ const tool = firstString(
223
+ stringValue(metadata?.toolName),
224
+ stringValue(metadata?.tool),
225
+ stringValue(input?.tool),
226
+ stringValue(input?.toolName),
227
+ stringValue(toolCall?.tool),
228
+ stringValue(toolCall?.toolName),
229
+ stringValue(toolCallRawInput?.tool),
230
+ stringValue(toolCallRawInput?.toolName),
231
+ parsedToolName?.tool
232
+ );
233
+ if (!server || !tool) {
234
+ return null;
235
+ }
236
+ return {
237
+ server,
238
+ tool,
239
+ displayName: formatAgentMcpToolTarget({ server, tool }),
240
+ instruction: firstString(
241
+ formatToolParamsDisplay(requestMeta?.tool_params_display),
242
+ formatObjectInstruction(requestMeta?.tool_params),
243
+ formatObjectInstruction(input?.arguments),
244
+ formatObjectInstruction(toolCallRawInput?.arguments)
245
+ ) ?? null
246
+ };
247
+ }
248
+ function formatAgentMcpToolTarget({
249
+ server,
250
+ tool
251
+ }) {
252
+ return `${server} / ${tool}`;
253
+ }
254
+ function parseMcpToolName(value) {
255
+ const match = value?.match(/^mcp__([^_]+(?:_[^_]+)*)__(.+)$/i);
256
+ if (!match) {
257
+ return null;
258
+ }
259
+ const server = match[1]?.replace(/_/gu, "-").trim();
260
+ const tool = match[2]?.trim();
261
+ return server && tool ? { server, tool } : null;
262
+ }
263
+ function firstString(...values) {
264
+ return values.find((value) => value !== null && value !== void 0) ?? null;
265
+ }
266
+ function formatToolParamsDisplay(value) {
267
+ if (!Array.isArray(value)) {
268
+ return null;
269
+ }
270
+ const parts = value.flatMap((entry) => {
271
+ const record = objectValue(entry);
272
+ const name = stringValue(record?.display_name) ?? stringValue(record?.displayName) ?? stringValue(record?.name);
273
+ const rawValue = record?.value;
274
+ const formattedValue = formatInstructionValue(rawValue);
275
+ return name && formattedValue ? [`${name}: ${formattedValue}`] : [];
276
+ });
277
+ return parts.length > 0 ? parts.join(", ") : null;
278
+ }
279
+ function formatObjectInstruction(value) {
280
+ const record = objectValue(value);
281
+ if (!record) {
282
+ return stringValue(value);
283
+ }
284
+ const parts = Object.entries(record).flatMap(([key, rawValue]) => {
285
+ const formattedValue = formatInstructionValue(rawValue);
286
+ return formattedValue ? [`${key}: ${formattedValue}`] : [];
287
+ });
288
+ return parts.length > 0 ? parts.join(", ") : null;
289
+ }
290
+ function formatInstructionValue(value) {
291
+ if (typeof value === "string") {
292
+ return stringValue(value);
293
+ }
294
+ if (typeof value === "number" || typeof value === "boolean" || value === null) {
295
+ return String(value);
296
+ }
297
+ if (Array.isArray(value)) {
298
+ const parts = value.flatMap((entry) => {
299
+ const formattedValue = formatInstructionValue(entry);
300
+ return formattedValue ? [formattedValue] : [];
301
+ });
302
+ return parts.length > 0 ? parts.join(", ") : null;
303
+ }
304
+ if (objectValue(value)) {
305
+ return stableJsonValue(value);
306
+ }
307
+ return null;
308
+ }
309
+ function stableJsonValue(value) {
310
+ try {
311
+ return JSON.stringify(value);
312
+ } catch {
313
+ return null;
314
+ }
315
+ }
316
+ function stringValue(value) {
317
+ return typeof value === "string" && value.trim() ? value.trim() : null;
318
+ }
319
+ function objectValue(value) {
320
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
321
+ }
322
+
323
+ // shared/agentConversation/askUserQuestions.ts
324
+ function normalizeAskUserQuestions(rawQuestions) {
325
+ return arrayValue(rawQuestions).flatMap((value, index) => {
326
+ const question = objectValue2(value);
327
+ if (!question) {
328
+ return [];
329
+ }
330
+ return [
331
+ {
332
+ id: stringValue2(question.id) ?? `question-${index + 1}`,
333
+ header: stringValue2(question.header) ?? `Question ${index + 1}`,
334
+ question: stringValue2(question.question) ?? stringValue2(question.header) ?? `Question ${index + 1}`,
335
+ options: arrayValue(question.options).flatMap((optionValue) => {
336
+ const option = objectValue2(optionValue);
337
+ const label = stringValue2(option?.label);
338
+ if (!label) {
339
+ return [];
340
+ }
341
+ return [
342
+ {
343
+ label,
344
+ description: stringValue2(option?.description) ?? ""
345
+ }
346
+ ];
347
+ }),
348
+ multiSelect: Boolean(question.multiSelect)
349
+ }
350
+ ];
351
+ });
352
+ }
353
+ function stringValue2(value) {
354
+ return typeof value === "string" && value.trim() ? value.trim() : null;
355
+ }
356
+ function objectValue2(value) {
357
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
358
+ }
359
+ function arrayValue(value) {
360
+ return Array.isArray(value) ? value : [];
361
+ }
362
+
363
+ // shared/AgentMessageMarkdown.tsx
364
+ import {
365
+ createContext as createContext3,
366
+ startTransition as startTransition2,
367
+ useCallback,
368
+ useEffect as useEffect2,
369
+ useContext as useContext3,
370
+ memo,
371
+ useMemo as useMemo2,
372
+ useState as useState2
373
+ } from "react";
374
+
375
+ // app/renderer/components/ZoomableImage.tsx
376
+ import {
377
+ cloneElement
378
+ } from "react";
379
+ import Zoom from "react-medium-image-zoom";
380
+
381
+ // app/renderer/lib/utils.ts
382
+ import { clsx } from "clsx";
383
+ import { twMerge } from "tailwind-merge";
384
+ function cn(...inputs) {
385
+ return twMerge(clsx(inputs));
386
+ }
387
+
388
+ // app/renderer/components/ZoomableImage.tsx
389
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
390
+ function ZoomableImage({
391
+ className,
392
+ wrapElement = "div",
393
+ ...props
394
+ }) {
395
+ const { t } = useTranslation();
396
+ const renderZoomContent = ({
397
+ buttonUnzoom,
398
+ img
399
+ }) => /* @__PURE__ */ jsxs(Fragment, { children: [
400
+ img,
401
+ cloneElement(buttonUnzoom, {
402
+ className: cn(
403
+ buttonUnzoom.props.className,
404
+ "nodrag tsh-desktop-no-drag"
405
+ )
406
+ })
407
+ ] });
408
+ return /* @__PURE__ */ jsx3(
409
+ Zoom,
410
+ {
411
+ a11yNameButtonZoom: t("common.expandImage"),
412
+ a11yNameButtonUnzoom: t("common.minimizeImage"),
413
+ classDialog: "tsh-zoom-dialog nodrag tsh-desktop-no-drag",
414
+ wrapElement,
415
+ zoomMargin: 24,
416
+ ZoomContent: renderZoomContent,
417
+ children: /* @__PURE__ */ jsx3(
418
+ "img",
419
+ {
420
+ ...props,
421
+ className: cn("nodrag tsh-desktop-no-drag cursor-zoom-in", className)
422
+ }
423
+ )
424
+ }
425
+ );
426
+ }
427
+
428
+ // shared/AgentMessageMarkdown.tsx
429
+ import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
430
+ import rehypeSanitize, {
431
+ defaultSchema
432
+ } from "rehype-sanitize";
433
+ import remarkGfm from "remark-gfm";
434
+ import {
435
+ resolveWorkspaceFileExtension,
436
+ resolveWorkspaceImageMimeType,
437
+ workspaceFileName as basenameWorkspacePath
438
+ } from "@tutti-os/workspace-file-manager/services";
439
+
440
+ // shared/utils/websiteUrl.ts
441
+ var ALLOWED_WEBSITE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
442
+ 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;
443
+ var EXPLICIT_PROTOCOL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//;
444
+ var LOOPBACK_HOST_PATTERN = /^(localhost|127(?:\.\d{1,3}){3})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
445
+ function defaultSchemeForHostInput(value) {
446
+ return LOOPBACK_HOST_PATTERN.test(value) ? "http" : "https";
447
+ }
448
+ function resolveWebsiteNavigationUrl(rawUrl) {
449
+ const trimmed = rawUrl.trim();
450
+ if (trimmed.length === 0) {
451
+ return { url: null, error: null };
452
+ }
453
+ if (EXPLICIT_PROTOCOL_PATTERN.test(trimmed)) {
454
+ try {
455
+ const parsed = new URL(trimmed);
456
+ if (!ALLOWED_WEBSITE_PROTOCOLS.has(parsed.protocol)) {
457
+ return { url: null, error: `Unsupported protocol: ${parsed.protocol}` };
458
+ }
459
+ return { url: parsed.toString(), error: null };
460
+ } catch {
461
+ return { url: null, error: "Invalid URL" };
462
+ }
463
+ }
464
+ if (!LIKELY_HOST_PATTERN.test(trimmed)) {
465
+ return { url: null, error: "Invalid URL" };
466
+ }
467
+ try {
468
+ const parsed = new URL(
469
+ `${defaultSchemeForHostInput(trimmed)}://${trimmed}`
470
+ );
471
+ return { url: parsed.toString(), error: null };
472
+ } catch {
473
+ return { url: null, error: "Invalid URL" };
474
+ }
475
+ }
476
+
477
+ // actions/workspaceLinkActions.ts
478
+ import {
479
+ parseWorkspaceIssueMentionHref
480
+ } from "@tutti-os/workspace-issue-manager/core";
481
+ var URL_LIKE_LINK_PATTERN = /^[a-zA-Z][a-zA-Z\d+.-]*:|^#/;
482
+ function resolveWorkspaceFilePathCandidate({
483
+ path,
484
+ workspaceRoot,
485
+ basePath
486
+ }) {
487
+ const rawPath = decodeWorkspaceLinkPath(path.trim());
488
+ const root = normalizeWorkspaceFilePath(workspaceRoot?.trim() ?? "");
489
+ if (!rawPath || !root || isUrlLikeWorkspaceFilePath(rawPath)) {
490
+ return null;
491
+ }
492
+ const normalizedPath = normalizeWorkspaceFilePath(rawPath);
493
+ const base = normalizeWorkspaceFilePath(basePath?.trim() || root);
494
+ const resolvedPath = isAbsoluteLocalPath(normalizedPath) ? normalizedPath : normalizeWorkspaceFilePath(`${base}/${normalizedPath}`);
495
+ if (!isInsideOrEqual(resolvedPath, root) && !isDirectAgentGeneratedImagePath(resolvedPath) && !isDirectWorkspaceAppFilePath(resolvedPath)) {
496
+ return null;
497
+ }
498
+ return {
499
+ path: resolvedPath,
500
+ directoryPath: resolvedPath === root ? root : dirname(resolvedPath),
501
+ workspaceRoot: root
502
+ };
503
+ }
504
+ function resolveWorkspaceFileLinkAction({
505
+ path,
506
+ workspaceRoot,
507
+ basePath,
508
+ source
509
+ }) {
510
+ const candidate = resolveWorkspaceFilePathCandidate({
511
+ path,
512
+ workspaceRoot,
513
+ basePath
514
+ });
515
+ if (!candidate) {
516
+ return null;
517
+ }
518
+ return {
519
+ type: "open-workspace-file",
520
+ path: candidate.path,
521
+ directoryPath: candidate.directoryPath,
522
+ workspaceRoot: candidate.workspaceRoot,
523
+ source
524
+ };
525
+ }
526
+ function resolveWorkspaceUrlLinkAction({
527
+ url,
528
+ source
529
+ }) {
530
+ const resolved = resolveWebsiteNavigationUrl(url);
531
+ if (!resolved.url || resolved.error) {
532
+ return null;
533
+ }
534
+ return {
535
+ type: "open-url",
536
+ url: resolved.url,
537
+ source
538
+ };
539
+ }
540
+ function resolveWorkspaceMentionLinkAction({
541
+ href,
542
+ source
543
+ }) {
544
+ const rawHref = href.trim();
545
+ if (!rawHref.toLowerCase().startsWith("mention://")) {
546
+ return null;
547
+ }
548
+ let url;
549
+ try {
550
+ url = new URL(rawHref);
551
+ } catch {
552
+ return null;
553
+ }
554
+ const resource = url.hostname.trim().toLowerCase();
555
+ const workspaceId = url.searchParams.get("workspaceId")?.trim() || "";
556
+ const targetId = url.searchParams.get("id")?.trim() || "";
557
+ if (!workspaceId || !targetId) {
558
+ return null;
559
+ }
560
+ if (resource === "agent-session") {
561
+ const provider = url.searchParams.get("provider")?.trim() || null;
562
+ return {
563
+ type: "open-agent-session",
564
+ workspaceId,
565
+ agentSessionId: targetId,
566
+ ...provider ? { provider } : {},
567
+ source
568
+ };
569
+ }
570
+ if (resource === "workspace-issue") {
571
+ const parsedIssueMention = parseWorkspaceIssueMentionHref(rawHref);
572
+ if (!parsedIssueMention) {
573
+ return null;
574
+ }
575
+ return {
576
+ type: "open-workspace-issue",
577
+ workspaceId: parsedIssueMention.workspaceId,
578
+ issueId: parsedIssueMention.issueId,
579
+ ...parsedIssueMention.mode ? { mode: parsedIssueMention.mode } : {},
580
+ ...parsedIssueMention.outputDir ? { outputDir: parsedIssueMention.outputDir } : {},
581
+ ...parsedIssueMention.runId ? { runId: parsedIssueMention.runId } : {},
582
+ ...parsedIssueMention.taskId ? { taskId: parsedIssueMention.taskId } : {},
583
+ ...parsedIssueMention.topicId ? { topicId: parsedIssueMention.topicId } : {},
584
+ source
585
+ };
586
+ }
587
+ return null;
588
+ }
589
+ function resolveWorkspaceLinkAction({
590
+ href,
591
+ workspaceRoot,
592
+ basePath,
593
+ source
594
+ }) {
595
+ return resolveWorkspaceMentionLinkAction({ href, source }) ?? resolveWorkspaceFileLinkAction({
596
+ path: href,
597
+ workspaceRoot,
598
+ basePath,
599
+ source
600
+ }) ?? resolveWorkspaceUrlLinkAction({ url: href, source });
601
+ }
602
+ function normalizeWorkspaceFilePath(path) {
603
+ const normalizedPath = path.trim().replaceAll("\\", "/");
604
+ const drive = /^[A-Za-z]:/.exec(normalizedPath)?.[0] ?? "";
605
+ const startsWithSlash = normalizedPath.startsWith("/");
606
+ const pathBody = drive ? normalizedPath.slice(drive.length) : startsWithSlash ? normalizedPath.slice(1) : normalizedPath;
607
+ const parts = [];
608
+ for (const part of pathBody.split("/")) {
609
+ if (!part || part === ".") {
610
+ continue;
611
+ }
612
+ if (part === "..") {
613
+ parts.pop();
614
+ continue;
615
+ }
616
+ parts.push(part);
617
+ }
618
+ if (drive) {
619
+ return parts.length > 0 ? `${drive}/${parts.join("/")}` : `${drive}/`;
620
+ }
621
+ if (startsWithSlash) {
622
+ return parts.length > 0 ? `/${parts.join("/")}` : "/";
623
+ }
624
+ return parts.join("/");
625
+ }
626
+ function isUrlLikeWorkspaceFilePath(path) {
627
+ if (path.startsWith("#")) {
628
+ return true;
629
+ }
630
+ if (isWindowsAbsolutePath(path.trim().replaceAll("\\", "/"))) {
631
+ return false;
632
+ }
633
+ return URL_LIKE_LINK_PATTERN.test(path);
634
+ }
635
+ function isAbsoluteLocalPath(path) {
636
+ return path.startsWith("/") || isWindowsAbsolutePath(path);
637
+ }
638
+ function isWindowsAbsolutePath(path) {
639
+ return /^[A-Za-z]:\//.test(path);
640
+ }
641
+ function decodeWorkspaceLinkPath(path) {
642
+ if (!path.includes("%")) {
643
+ return path;
644
+ }
645
+ try {
646
+ return decodeURI(path);
647
+ } catch {
648
+ return path;
649
+ }
650
+ }
651
+ function dirname(path) {
652
+ const index = path.lastIndexOf("/");
653
+ if (index <= 0) {
654
+ return "/";
655
+ }
656
+ return path.slice(0, index);
657
+ }
658
+ function isInsideOrEqual(path, root) {
659
+ if (root === "/") {
660
+ return path.startsWith("/");
661
+ }
662
+ const comparison = isWindowsAbsolutePath(root) || isWindowsAbsolutePath(path) ? { path: path.toLowerCase(), root: root.toLowerCase() } : { path, root };
663
+ return comparison.path === comparison.root || comparison.path.startsWith(`${comparison.root}/`);
664
+ }
665
+ function isDirectAgentGeneratedImagePath(path) {
666
+ if (!isAbsoluteLocalPath(path)) {
667
+ return false;
668
+ }
669
+ const segments = path.split("/").filter(Boolean);
670
+ const stateRootIndex = segments.findIndex(
671
+ (segment) => segment === ".tutti" || segment === ".tutti-dev"
672
+ );
673
+ if (stateRootIndex < 0) {
674
+ return false;
675
+ }
676
+ const statePath = segments.slice(stateRootIndex);
677
+ if (statePath[1] !== "agent" || statePath[2] !== "runs" || !statePath.includes("generated_images")) {
678
+ return false;
679
+ }
680
+ return /\.(?:png|jpe?g|gif|webp|bmp)$/i.test(path);
681
+ }
682
+ function isDirectWorkspaceAppFilePath(path) {
683
+ if (!isAbsoluteLocalPath(path)) {
684
+ return false;
685
+ }
686
+ const segments = path.split("/").filter(Boolean);
687
+ const stateRootIndex = segments.findIndex(
688
+ (segment) => segment === ".tutti" || segment === ".tutti-dev"
689
+ );
690
+ if (stateRootIndex < 0) {
691
+ return false;
692
+ }
693
+ const statePath = segments.slice(stateRootIndex);
694
+ if (statePath[1] !== "apps") {
695
+ return false;
696
+ }
697
+ if (statePath[2] === "workspaces") {
698
+ return statePath.length > 6 && statePath[5] === "data";
699
+ }
700
+ if (statePath[2] === "packages") {
701
+ return statePath.length > 5;
702
+ }
703
+ return false;
704
+ }
705
+
706
+ // shared/streamingMarkdownTailStabilizer.ts
707
+ var DEFAULT_MAX_TAIL_CHARS = 4096;
708
+ function stabilizeStreamingMarkdownTail(content, options) {
709
+ if (!options.streaming || content.length === 0) {
710
+ return { content, changed: false };
711
+ }
712
+ const maxTailChars = Math.max(
713
+ 256,
714
+ options.maxTailChars ?? DEFAULT_MAX_TAIL_CHARS
715
+ );
716
+ const tailStart = Math.max(0, content.length - maxTailChars);
717
+ const tail = content.slice(tailStart);
718
+ const fence = findOpenFence(tail);
719
+ if (fence) {
720
+ return {
721
+ content: `${content}
722
+ ${fence.marker.repeat(fence.length)}`,
723
+ changed: true,
724
+ reason: "open-fence"
725
+ };
726
+ }
727
+ const incompleteLink = stabilizeIncompleteTailLink(content);
728
+ if (incompleteLink) {
729
+ return incompleteLink;
730
+ }
731
+ const listMarker = stabilizeDanglingListMarker(content);
732
+ if (listMarker) {
733
+ return listMarker;
734
+ }
735
+ const tableRow = stabilizePartialTableRow(content);
736
+ if (tableRow) {
737
+ return tableRow;
738
+ }
739
+ const inlineCode = findOpenInlineCodeSpan(tail);
740
+ if (inlineCode) {
741
+ return {
742
+ content: `${content}${"`".repeat(inlineCode.length)}`,
743
+ changed: true,
744
+ reason: "open-inline-code"
745
+ };
746
+ }
747
+ const emphasis = stabilizeTrailingEmphasisMarker(content);
748
+ if (emphasis) {
749
+ return emphasis;
750
+ }
751
+ return { content, changed: false };
752
+ }
753
+ function findOpenFence(tail) {
754
+ let openFence = null;
755
+ for (const line of tail.replace(/\r\n?/g, "\n").split("\n")) {
756
+ const fence = parseFenceLine(line);
757
+ if (!fence) {
758
+ continue;
759
+ }
760
+ if (!openFence) {
761
+ openFence = fence;
762
+ continue;
763
+ }
764
+ if (fence.marker === openFence.marker && fence.length >= openFence.length) {
765
+ openFence = null;
766
+ }
767
+ }
768
+ return openFence;
769
+ }
770
+ function parseFenceLine(line) {
771
+ let index = 0;
772
+ while (index < line.length && line[index] === " " && index < 4) {
773
+ index += 1;
774
+ }
775
+ const marker = line[index];
776
+ if (marker !== "`" && marker !== "~") {
777
+ return null;
778
+ }
779
+ let length = 0;
780
+ while (line[index + length] === marker) {
781
+ length += 1;
782
+ }
783
+ return length >= 3 ? { marker, length } : null;
784
+ }
785
+ function stabilizeIncompleteTailLink(content) {
786
+ const lineStart = content.lastIndexOf("\n") + 1;
787
+ const line = content.slice(lineStart);
788
+ const linkStart = line.lastIndexOf("[");
789
+ const imageStart = line.lastIndexOf("![");
790
+ const openBracketIndex = imageStart >= 0 && imageStart + 1 === linkStart ? imageStart : linkStart;
791
+ if (openBracketIndex < 0) {
792
+ return null;
793
+ }
794
+ const absoluteOpenIndex = lineStart + openBracketIndex;
795
+ const suffix = content.slice(absoluteOpenIndex);
796
+ const closeLabelIndex = suffix.indexOf("]");
797
+ if (closeLabelIndex < 0) {
798
+ const label2 = suffix.startsWith("![") ? suffix.slice(2) : suffix.slice(1);
799
+ return {
800
+ content: `${content.slice(0, absoluteOpenIndex)}${label2}`,
801
+ changed: true,
802
+ reason: "incomplete-link-label"
803
+ };
804
+ }
805
+ if (suffix[closeLabelIndex + 1] !== "(" || suffix.includes(")")) {
806
+ return null;
807
+ }
808
+ const label = suffix.startsWith("![") ? suffix.slice(2, closeLabelIndex) : suffix.slice(1, closeLabelIndex);
809
+ return {
810
+ content: `${content.slice(0, absoluteOpenIndex)}${label}`,
811
+ changed: true,
812
+ reason: "incomplete-link-target"
813
+ };
814
+ }
815
+ function stabilizeDanglingListMarker(content) {
816
+ const lineStart = content.lastIndexOf("\n") + 1;
817
+ const line = content.slice(lineStart);
818
+ const trimmed = line.trim();
819
+ const isBullet = trimmed === "-" || trimmed === "*" || trimmed === "+";
820
+ const isOrdered = trimmed.length >= 2 && trimmed.endsWith(".") && [...trimmed.slice(0, -1)].every((char) => char >= "0" && char <= "9");
821
+ if (!isBullet && !isOrdered) {
822
+ return null;
823
+ }
824
+ return {
825
+ content: content.slice(0, lineStart),
826
+ changed: true,
827
+ reason: "dangling-list-marker"
828
+ };
829
+ }
830
+ function stabilizePartialTableRow(content) {
831
+ if (content.endsWith("\n")) {
832
+ return null;
833
+ }
834
+ const lines = content.replace(/\r\n?/g, "\n").split("\n");
835
+ const currentLine = lines.at(-1) ?? "";
836
+ const previousLine = lines.at(-2) ?? "";
837
+ if (!currentLine.includes("|") || !previousLine.includes("|")) {
838
+ return null;
839
+ }
840
+ if (currentLine.trimEnd().endsWith("|")) {
841
+ return null;
842
+ }
843
+ if (countChar(previousLine, "|") < 2) {
844
+ return null;
845
+ }
846
+ return {
847
+ content: `${content} |`,
848
+ changed: true,
849
+ reason: "partial-table-row"
850
+ };
851
+ }
852
+ function findOpenInlineCodeSpan(tail) {
853
+ const lastParagraph = tail.slice(tail.lastIndexOf("\n\n") + 2);
854
+ let openLength = 0;
855
+ for (let index = 0; index < lastParagraph.length; index += 1) {
856
+ if (lastParagraph[index] !== "`") {
857
+ continue;
858
+ }
859
+ let length = 1;
860
+ while (lastParagraph[index + length] === "`") {
861
+ length += 1;
862
+ }
863
+ if (length >= 3) {
864
+ index += length - 1;
865
+ continue;
866
+ }
867
+ openLength = openLength === length ? 0 : length;
868
+ index += length - 1;
869
+ }
870
+ return openLength > 0 ? { length: openLength } : null;
871
+ }
872
+ function stabilizeTrailingEmphasisMarker(content) {
873
+ const marker = content.at(-1);
874
+ if (marker !== "*" && marker !== "_") {
875
+ return null;
876
+ }
877
+ const previous = content.at(-2);
878
+ const nextContent = previous === marker ? content.slice(0, -2) : content.slice(0, -1);
879
+ return {
880
+ content: nextContent,
881
+ changed: true,
882
+ reason: "trailing-emphasis-marker"
883
+ };
884
+ }
885
+ function countChar(value, char) {
886
+ let count = 0;
887
+ for (const current of value) {
888
+ if (current === char) {
889
+ count += 1;
890
+ }
891
+ }
892
+ return count;
893
+ }
894
+
895
+ // shared/useStreamingVisibleText.ts
896
+ import {
897
+ startTransition,
898
+ useEffect,
899
+ useRef,
900
+ useState
901
+ } from "react";
902
+ var DEFAULT_FRAME_MS = 24;
903
+ var DEFAULT_MAX_CHARS_PER_SECOND = 6e3;
904
+ var DEFAULT_TRAILING_FLUSH_CHARS = 0;
905
+ function useStreamingVisibleText(sourceText, options) {
906
+ const {
907
+ enabled,
908
+ frameMs = DEFAULT_FRAME_MS,
909
+ maxCharsPerSecond = DEFAULT_MAX_CHARS_PER_SECOND,
910
+ trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
911
+ } = options;
912
+ const [visibleText, setVisibleText] = useState(sourceText);
913
+ const sourceRef = useRef(sourceText);
914
+ const visibleRef = useRef(visibleText);
915
+ const timerRef = useRef(null);
916
+ useEffect(() => {
917
+ visibleRef.current = visibleText;
918
+ }, [visibleText]);
919
+ useEffect(
920
+ () => () => {
921
+ clearStreamingVisibleTextTimer(timerRef);
922
+ },
923
+ []
924
+ );
925
+ useEffect(() => {
926
+ sourceRef.current = sourceText;
927
+ if (!enabled) {
928
+ clearStreamingVisibleTextTimer(timerRef);
929
+ visibleRef.current = sourceText;
930
+ setVisibleText(sourceText);
931
+ return;
932
+ }
933
+ if (sourceText === visibleRef.current || timerRef.current !== null) {
934
+ return;
935
+ }
936
+ timerRef.current = setTimeout(
937
+ () => {
938
+ timerRef.current = null;
939
+ const nextVisibleText = advanceStreamingVisibleText({
940
+ visibleText: visibleRef.current,
941
+ sourceText: sourceRef.current,
942
+ frameMs,
943
+ maxCharsPerSecond,
944
+ trailingFlushChars
945
+ });
946
+ if (nextVisibleText === visibleRef.current) {
947
+ return;
948
+ }
949
+ visibleRef.current = nextVisibleText;
950
+ startTransition(() => {
951
+ setVisibleText(nextVisibleText);
952
+ });
953
+ },
954
+ Math.max(1, frameMs)
955
+ );
956
+ return void 0;
957
+ }, [
958
+ enabled,
959
+ frameMs,
960
+ maxCharsPerSecond,
961
+ sourceText,
962
+ trailingFlushChars,
963
+ visibleText
964
+ ]);
965
+ return enabled ? visibleText : sourceText;
966
+ }
967
+ function advanceStreamingVisibleText({
968
+ visibleText,
969
+ sourceText,
970
+ frameMs = DEFAULT_FRAME_MS,
971
+ maxCharsPerSecond = DEFAULT_MAX_CHARS_PER_SECOND,
972
+ trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
973
+ }) {
974
+ if (visibleText === sourceText) {
975
+ return visibleText;
976
+ }
977
+ const prefixLength = sourceText.startsWith(visibleText) ? visibleText.length : commonPrefixLength(visibleText, sourceText);
978
+ const stablePrefix = sourceText.slice(0, prefixLength);
979
+ const remainingLength = sourceText.length - prefixLength;
980
+ if (remainingLength <= trailingFlushChars) {
981
+ return sourceText;
982
+ }
983
+ const charsPerFrame = Math.max(
984
+ 1,
985
+ Math.ceil(Math.max(1, maxCharsPerSecond) * Math.max(1, frameMs) / 1e3)
986
+ );
987
+ return sourceText.slice(
988
+ 0,
989
+ Math.min(sourceText.length, stablePrefix.length + charsPerFrame)
990
+ );
991
+ }
992
+ function commonPrefixLength(left, right) {
993
+ const maxLength = Math.min(left.length, right.length);
994
+ for (let index = 0; index < maxLength; index += 1) {
995
+ if (left.charCodeAt(index) !== right.charCodeAt(index)) {
996
+ return index;
997
+ }
998
+ }
999
+ return maxLength;
1000
+ }
1001
+ function clearStreamingVisibleTextTimer(timerRef) {
1002
+ if (timerRef.current === null) {
1003
+ return;
1004
+ }
1005
+ clearTimeout(timerRef.current);
1006
+ timerRef.current = null;
1007
+ }
1008
+
1009
+ // shared/AgentMessageMarkdown.tsx
1010
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1011
+ var COLLAPSED_LINE_LIMIT = 8;
1012
+ var APPROX_CHARS_PER_LINE = 34;
1013
+ var DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD = 4096;
1014
+ var STREAMING_MARKDOWN_EMERGENCY_PLAIN_CHAR_THRESHOLD = 96e3;
1015
+ var DEFERRED_LONG_MARKDOWN_FALLBACK_DELAY_MS = 80;
1016
+ var DEFERRED_LONG_MARKDOWN_IDLE_TIMEOUT_MS = 700;
1017
+ var STREAMING_MARKDOWN_FRAME_MS = 24;
1018
+ var STREAMING_MARKDOWN_MAX_CHARS_PER_SECOND = 6e3;
1019
+ var STREAMING_MARKDOWN_TAIL_FLUSH_CHARS = 0;
1020
+ var PLAIN_SESSION_MENTION_AGENT_LABELS = [
1021
+ "Claude Code",
1022
+ "Nexight",
1023
+ "Codex"
1024
+ ];
1025
+ var MARKDOWN_SANITIZE_SCHEMA = {
1026
+ ...defaultSchema,
1027
+ protocols: {
1028
+ ...defaultSchema.protocols,
1029
+ href: [...defaultSchema.protocols?.href ?? [], "mention"]
1030
+ }
1031
+ };
1032
+ var EMPTY_WORKSPACE_APP_ICONS = [];
1033
+ var MarkdownLinkContext = createContext3(false);
1034
+ function AgentMessageMarkdown({
1035
+ content,
1036
+ onLinkClick,
1037
+ onLinkAction,
1038
+ workspaceLinkContext = null,
1039
+ workspaceAppIcons = EMPTY_WORKSPACE_APP_ICONS,
1040
+ collapsible = false,
1041
+ expandLabel,
1042
+ className,
1043
+ inline = false,
1044
+ normalizePlainIssueMentionTitle = false,
1045
+ deferLongContentRender = false,
1046
+ enableImageZoom = false,
1047
+ streaming = false
1048
+ }) {
1049
+ "use memo";
1050
+ const { t } = useTranslation();
1051
+ const visibleContent = useStreamingVisibleText(content, {
1052
+ enabled: streaming,
1053
+ frameMs: STREAMING_MARKDOWN_FRAME_MS,
1054
+ maxCharsPerSecond: STREAMING_MARKDOWN_MAX_CHARS_PER_SECOND,
1055
+ trailingFlushChars: STREAMING_MARKDOWN_TAIL_FLUSH_CHARS
1056
+ });
1057
+ const stabilizedContent = useMemo2(
1058
+ () => stabilizeStreamingMarkdownTail(visibleContent, {
1059
+ streaming
1060
+ }).content,
1061
+ [streaming, visibleContent]
1062
+ );
1063
+ const workspaceRoot = workspaceLinkContext?.workspaceRoot ?? null;
1064
+ const basePath = workspaceLinkContext?.basePath ?? null;
1065
+ const workspaceLinkSource = workspaceLinkContext?.source ?? null;
1066
+ const [isExpanded, setIsExpanded] = useState2(false);
1067
+ const resolvedExpandLabel = expandLabel ?? t("agentHost.workspaceAgentMessageExpand");
1068
+ const shouldCollapse = collapsible && isLikelyLongerThanLineLimit(stabilizedContent);
1069
+ const isCollapsed = shouldCollapse && !isExpanded;
1070
+ const ContainerTag = inline ? "span" : "div";
1071
+ const contentSignature = useMemo2(
1072
+ () => hashMarkdownProfilerContent(stabilizedContent),
1073
+ [stabilizedContent]
1074
+ );
1075
+ const normalizedContent = useMemo2(
1076
+ () => linkBareLocalAbsolutePaths(
1077
+ normalizeMentionMarkdownLinks(
1078
+ normalizePlainIssueMentionTitle ? normalizePlainIssueMentionTitleContent(
1079
+ normalizePlainSessionMentionTitle(stabilizedContent)
1080
+ ) : normalizePlainSessionMentionTitle(stabilizedContent)
1081
+ )
1082
+ ),
1083
+ [normalizePlainIssueMentionTitle, stabilizedContent]
1084
+ );
1085
+ const isMentionOnly = isMentionOnlyMarkdownContent(normalizedContent);
1086
+ const shouldDeferMarkdownRender = deferLongContentRender && !inline && content.length >= (streaming ? STREAMING_MARKDOWN_EMERGENCY_PLAIN_CHAR_THRESHOLD : DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD) && !isExpanded;
1087
+ const markdownRenderReady = useDeferredMarkdownRenderReady(
1088
+ contentSignature,
1089
+ shouldDeferMarkdownRender
1090
+ );
1091
+ const handleLinkClick = useCallback(
1092
+ (href) => {
1093
+ if (workspaceLinkSource && onLinkAction) {
1094
+ const action = resolveWorkspaceLinkAction({
1095
+ href,
1096
+ workspaceRoot,
1097
+ basePath,
1098
+ source: workspaceLinkSource
1099
+ });
1100
+ if (action) {
1101
+ onLinkAction(action);
1102
+ return;
1103
+ }
1104
+ }
1105
+ onLinkClick?.(href);
1106
+ },
1107
+ [basePath, onLinkAction, onLinkClick, workspaceLinkSource, workspaceRoot]
1108
+ );
1109
+ const handleAnchorClickCapture = useCallback(
1110
+ (event) => {
1111
+ const href = resolveMarkdownAnchorHref(event.target);
1112
+ if (!href) {
1113
+ return;
1114
+ }
1115
+ event.preventDefault();
1116
+ event.stopPropagation();
1117
+ handleLinkClick(href);
1118
+ },
1119
+ [handleLinkClick]
1120
+ );
1121
+ const markdownComponents = useMemo2(
1122
+ () => ({
1123
+ a: (props) => /* @__PURE__ */ jsx4(
1124
+ MarkdownLink,
1125
+ {
1126
+ ...props,
1127
+ onLinkClick: handleLinkClick,
1128
+ workspaceAppIcons
1129
+ }
1130
+ ),
1131
+ code: (props) => /* @__PURE__ */ jsx4(MarkdownCode, { ...props, onLinkClick: handleLinkClick }),
1132
+ img: (props) => /* @__PURE__ */ jsx4(MarkdownImage, { ...props, enableZoom: enableImageZoom }),
1133
+ p: (props) => /* @__PURE__ */ jsx4(MarkdownParagraph, { ...props, inline }),
1134
+ ul: MarkdownUnorderedList,
1135
+ ol: MarkdownOrderedList,
1136
+ li: MarkdownListItem
1137
+ }),
1138
+ [enableImageZoom, handleLinkClick, inline, workspaceAppIcons]
1139
+ );
1140
+ return /* @__PURE__ */ jsxs2(
1141
+ ContainerTag,
1142
+ {
1143
+ className: "flex w-full min-w-0 flex-col items-start gap-1",
1144
+ "data-workspace-agent-markdown-shell": "true",
1145
+ children: [
1146
+ /* @__PURE__ */ jsx4(
1147
+ ContainerTag,
1148
+ {
1149
+ className: cn(
1150
+ "relative w-full min-w-0 overflow-x-auto text-[13px] leading-[1.5] text-[var(--text-primary)] [overflow-wrap:anywhere]",
1151
+ "[&_>table:first-child]:mt-0 [&_p]:mb-2 [&_pre]:mb-2 [&_blockquote]:mb-2",
1152
+ "[&_hr]:my-4 [&_hr]:h-0 [&_hr]:border-0 [&_hr]:border-t [&_hr]:border-t-[color-mix(in_srgb,var(--text-primary)_14%,transparent)]",
1153
+ "[&_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",
1154
+ "[&_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",
1155
+ "[&_li>ul]:mt-1.5 [&_li>ul]:mb-0.5 [&_li>ul]:border-0 [&_li>ul]:px-0 [&_li>ul]:py-1",
1156
+ "[&_li>ol]:mt-1.5 [&_li>ol]:mb-0.5 [&_li>ol]:border-0 [&_li>ol]:px-0 [&_li>ol]:py-1",
1157
+ "[&_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]",
1158
+ "[&_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))]",
1159
+ "[&_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]",
1160
+ "[&_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",
1161
+ "[&_a]:cursor-pointer [&_a]:font-semibold [&_a]:text-[var(--tutti-purple)] [&_a]:no-underline [&_a:hover]:underline [&_a:focus-visible]:underline",
1162
+ "[&_strong]:font-semibold",
1163
+ "[&_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]",
1164
+ "[&_pre]:box-border [&_pre]:overflow-auto [&_pre]:rounded-[6px] [&_pre]:bg-[var(--transparency-block)] [&_pre]:px-2.5 [&_pre]:py-2",
1165
+ "[&_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]",
1166
+ "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
1167
+ inline && "inline min-w-0 overflow-hidden align-baseline [&_p]:inline [&_p]:m-0",
1168
+ shouldCollapse && "overflow-hidden transition-[max-height] duration-220 ease-out",
1169
+ 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",
1170
+ shouldCollapse && !isCollapsed && "max-h-[72rem]",
1171
+ className
1172
+ ),
1173
+ "data-workspace-agent-markdown": "true",
1174
+ "data-agent-mention-only": isMentionOnly ? "true" : void 0,
1175
+ "data-collapsed": isCollapsed ? "true" : "false",
1176
+ onClickCapture: handleAnchorClickCapture,
1177
+ children: markdownRenderReady ? streaming ? /* @__PURE__ */ jsx4(
1178
+ StreamingMarkdownBlocks,
1179
+ {
1180
+ content: normalizedContent,
1181
+ components: markdownComponents
1182
+ }
1183
+ ) : /* @__PURE__ */ jsx4(
1184
+ ReactMarkdown,
1185
+ {
1186
+ remarkPlugins: [remarkGfm],
1187
+ rehypePlugins: [[rehypeSanitize, MARKDOWN_SANITIZE_SCHEMA]],
1188
+ urlTransform: markdownUrlTransform,
1189
+ components: markdownComponents,
1190
+ children: normalizedContent
1191
+ }
1192
+ ) : /* @__PURE__ */ jsx4(
1193
+ "div",
1194
+ {
1195
+ className: "whitespace-pre-wrap [overflow-wrap:anywhere]",
1196
+ "data-workspace-agent-markdown-deferred": "true",
1197
+ children: normalizedContent
1198
+ }
1199
+ )
1200
+ }
1201
+ ),
1202
+ shouldCollapse && !isExpanded ? /* @__PURE__ */ jsx4(
1203
+ "button",
1204
+ {
1205
+ type: "button",
1206
+ 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",
1207
+ onClick: () => setIsExpanded(true),
1208
+ children: resolvedExpandLabel
1209
+ }
1210
+ ) : null
1211
+ ]
1212
+ }
1213
+ );
1214
+ }
1215
+ function StreamingMarkdownBlocks({
1216
+ content,
1217
+ components
1218
+ }) {
1219
+ const blocks = useMemo2(
1220
+ () => splitStreamingMarkdownBlocks(content),
1221
+ [content]
1222
+ );
1223
+ return /* @__PURE__ */ jsx4(Fragment2, { children: blocks.map((block, index) => /* @__PURE__ */ jsx4(
1224
+ MemoizedMarkdownBlock,
1225
+ {
1226
+ content: block.content,
1227
+ components
1228
+ },
1229
+ `${index}:${hashMarkdownProfilerContent(block.initialKeyContent)}`
1230
+ )) });
1231
+ }
1232
+ var MemoizedMarkdownBlock = memo(function MemoizedMarkdownBlock2({
1233
+ content,
1234
+ components
1235
+ }) {
1236
+ return /* @__PURE__ */ jsx4(
1237
+ ReactMarkdown,
1238
+ {
1239
+ remarkPlugins: [remarkGfm],
1240
+ rehypePlugins: [[rehypeSanitize, MARKDOWN_SANITIZE_SCHEMA]],
1241
+ urlTransform: markdownUrlTransform,
1242
+ components,
1243
+ children: content
1244
+ }
1245
+ );
1246
+ });
1247
+ function splitStreamingMarkdownBlocks(content) {
1248
+ const normalized = content.replace(/\r\n?/g, "\n");
1249
+ if (!normalized) {
1250
+ return [{ content: "", initialKeyContent: "" }];
1251
+ }
1252
+ const lines = normalized.split("\n");
1253
+ const blocks = [];
1254
+ const current = [];
1255
+ let fence = null;
1256
+ for (const line of lines) {
1257
+ current.push(line);
1258
+ const lineFence = parseStreamingFence(line);
1259
+ if (lineFence) {
1260
+ if (!fence) {
1261
+ fence = lineFence;
1262
+ } else if (lineFence.marker === fence.marker && lineFence.length >= fence.length) {
1263
+ fence = null;
1264
+ }
1265
+ continue;
1266
+ }
1267
+ if (!fence && line.trim() === "") {
1268
+ pushStreamingMarkdownBlock(blocks, current);
1269
+ }
1270
+ }
1271
+ pushStreamingMarkdownBlock(blocks, current);
1272
+ return blocks.length > 0 ? blocks : [{ content: normalized, initialKeyContent: normalized }];
1273
+ }
1274
+ function pushStreamingMarkdownBlock(blocks, lines) {
1275
+ if (lines.length === 0) {
1276
+ return;
1277
+ }
1278
+ const content = lines.join("\n");
1279
+ if (!content) {
1280
+ lines.length = 0;
1281
+ return;
1282
+ }
1283
+ blocks.push({
1284
+ content,
1285
+ initialKeyContent: content
1286
+ });
1287
+ lines.length = 0;
1288
+ }
1289
+ function parseStreamingFence(line) {
1290
+ const trimmed = line.trimStart();
1291
+ const marker = trimmed[0];
1292
+ if (marker !== "`" && marker !== "~") {
1293
+ return null;
1294
+ }
1295
+ let length = 0;
1296
+ while (trimmed[length] === marker) {
1297
+ length += 1;
1298
+ }
1299
+ return length >= 3 ? { marker, length } : null;
1300
+ }
1301
+ function resolveMarkdownAnchorHref(target) {
1302
+ if (!(target instanceof Element)) {
1303
+ return null;
1304
+ }
1305
+ const link = target.closest("[data-agent-link-href],a[href]");
1306
+ if (!(link instanceof HTMLElement)) {
1307
+ return null;
1308
+ }
1309
+ const dataHref = link.dataset.agentLinkHref?.trim();
1310
+ if (dataHref) {
1311
+ return dataHref;
1312
+ }
1313
+ if (link instanceof HTMLAnchorElement) {
1314
+ return link.getAttribute("href")?.trim() || null;
1315
+ }
1316
+ return null;
1317
+ }
1318
+ function activateMarkdownLink(event, href, onLinkClick) {
1319
+ const target = href.trim();
1320
+ if (!target) {
1321
+ return;
1322
+ }
1323
+ event.preventDefault();
1324
+ event.stopPropagation();
1325
+ onLinkClick?.(target);
1326
+ }
1327
+ function activateMarkdownLinkFromKey(event, href, onLinkClick) {
1328
+ if (event.key !== "Enter" && event.key !== " ") {
1329
+ return;
1330
+ }
1331
+ activateMarkdownLink(event, href, onLinkClick);
1332
+ }
1333
+ function activateMarkdownLinkFromPointer(event, href, onLinkClick) {
1334
+ if (event.button !== 0) {
1335
+ return;
1336
+ }
1337
+ activateMarkdownLink(event, href, onLinkClick);
1338
+ }
1339
+ function useDeferredMarkdownRenderReady(contentSignature, shouldDefer) {
1340
+ const [readySignature, setReadySignature] = useState2(
1341
+ shouldDefer ? null : contentSignature
1342
+ );
1343
+ const renderReady = !shouldDefer || readySignature === contentSignature;
1344
+ useEffect2(() => {
1345
+ if (!shouldDefer) {
1346
+ setReadySignature(contentSignature);
1347
+ return;
1348
+ }
1349
+ let canceled = false;
1350
+ let timeoutId = null;
1351
+ let idleCallbackId = null;
1352
+ const markReady = () => {
1353
+ if (canceled) {
1354
+ return;
1355
+ }
1356
+ startTransition2(() => {
1357
+ setReadySignature(contentSignature);
1358
+ });
1359
+ };
1360
+ if ("requestIdleCallback" in window) {
1361
+ idleCallbackId = window.requestIdleCallback(markReady, {
1362
+ timeout: DEFERRED_LONG_MARKDOWN_IDLE_TIMEOUT_MS
1363
+ });
1364
+ } else {
1365
+ timeoutId = setTimeout(
1366
+ markReady,
1367
+ DEFERRED_LONG_MARKDOWN_FALLBACK_DELAY_MS
1368
+ );
1369
+ }
1370
+ return () => {
1371
+ canceled = true;
1372
+ if (idleCallbackId !== null) {
1373
+ window.cancelIdleCallback(idleCallbackId);
1374
+ }
1375
+ if (timeoutId !== null) {
1376
+ clearTimeout(timeoutId);
1377
+ }
1378
+ };
1379
+ }, [contentSignature, shouldDefer]);
1380
+ return renderReady;
1381
+ }
1382
+ function hashMarkdownProfilerContent(content) {
1383
+ let hash = 0;
1384
+ for (let index = 0; index < content.length; index += 1) {
1385
+ hash = hash * 31 + content.charCodeAt(index) | 0;
1386
+ }
1387
+ return `${content.length}:${Math.abs(hash)}`;
1388
+ }
1389
+ function isLikelyLongerThanLineLimit(content) {
1390
+ const normalizedLines = content.replace(/\r\n?/g, "\n").split("\n");
1391
+ if (normalizedLines.length > COLLAPSED_LINE_LIMIT) {
1392
+ return true;
1393
+ }
1394
+ const estimatedLineCount = normalizedLines.reduce((total, line) => {
1395
+ const trimmed = line.trim();
1396
+ const blockSpacing = /^(#{1,6}\s|\s*[-*+]\s|\s*\d+\.\s|>)/.test(trimmed) ? 1 : 0;
1397
+ return total + Math.max(1, Math.ceil(trimmed.length / APPROX_CHARS_PER_LINE)) + blockSpacing;
1398
+ }, 0);
1399
+ return estimatedLineCount > COLLAPSED_LINE_LIMIT;
1400
+ }
1401
+ function MarkdownLink({
1402
+ node: _node,
1403
+ onClick: _onClick,
1404
+ onLinkClick,
1405
+ workspaceAppIcons,
1406
+ href,
1407
+ ...props
1408
+ }) {
1409
+ "use memo";
1410
+ const { t } = useTranslation();
1411
+ const targetHref = href?.trim() ?? "";
1412
+ const mention = targetHref ? parseMentionLink(
1413
+ targetHref,
1414
+ textFromReactNode(props.children),
1415
+ workspaceAppIcons ?? [],
1416
+ t("agentHost.agentGui.workspaceAppFactoryMentionFallback")
1417
+ ) : null;
1418
+ if (mention) {
1419
+ return /* @__PURE__ */ jsx4(
1420
+ MentionLink,
1421
+ {
1422
+ ...props,
1423
+ href: targetHref,
1424
+ mention,
1425
+ onLinkClick
1426
+ }
1427
+ );
1428
+ }
1429
+ const fileMention = targetHref ? parseWorkspaceFileMentionLink(
1430
+ targetHref,
1431
+ textFromReactNode(props.children)
1432
+ ) : null;
1433
+ if (fileMention) {
1434
+ return /* @__PURE__ */ jsx4(
1435
+ WorkspaceFileMentionLink,
1436
+ {
1437
+ ...props,
1438
+ href: targetHref,
1439
+ mention: fileMention,
1440
+ onLinkClick
1441
+ }
1442
+ );
1443
+ }
1444
+ return /* @__PURE__ */ jsx4(MarkdownLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsx4(
1445
+ "a",
1446
+ {
1447
+ ...props,
1448
+ "data-agent-link-href": targetHref,
1449
+ role: "link",
1450
+ tabIndex: 0,
1451
+ onClick: (event) => {
1452
+ activateMarkdownLink(event, targetHref, onLinkClick);
1453
+ },
1454
+ onPointerDown: (event) => {
1455
+ activateMarkdownLinkFromPointer(event, targetHref, onLinkClick);
1456
+ },
1457
+ onKeyDown: (event) => {
1458
+ activateMarkdownLinkFromKey(event, targetHref, onLinkClick);
1459
+ }
1460
+ }
1461
+ ) });
1462
+ }
1463
+ function parseWorkspaceFileMentionTarget(href) {
1464
+ const target = href.trim();
1465
+ if (!isLocalAbsolutePath(target)) {
1466
+ return null;
1467
+ }
1468
+ let url;
1469
+ try {
1470
+ url = new URL(target, "https://tsh.local");
1471
+ } catch {
1472
+ return null;
1473
+ }
1474
+ const explicitKindValue = url.searchParams.get("kind") ?? url.searchParams.get("refType") ?? url.searchParams.get("entryKind") ?? "";
1475
+ const normalizedKind = explicitKindValue.trim().toLowerCase();
1476
+ const explicitKind = normalizedKind === "folder" || normalizedKind === "directory" ? "directory" : normalizedKind === "file" ? "file" : null;
1477
+ const path = url.pathname.replace(/\/+$/, "");
1478
+ if (!path || path === "/") {
1479
+ return null;
1480
+ }
1481
+ return {
1482
+ path,
1483
+ explicitKind: explicitKind ?? (url.pathname.endsWith("/") ? "directory" : null)
1484
+ };
1485
+ }
1486
+ function resolveWorkspaceFileMentionEntryKind(path, label, explicitKind) {
1487
+ if (explicitKind) {
1488
+ return explicitKind;
1489
+ }
1490
+ if (resolveWorkspaceFileExtension(path)) {
1491
+ return "file";
1492
+ }
1493
+ const basename = basenameWorkspacePath(path);
1494
+ return basename === label ? "directory" : "file";
1495
+ }
1496
+ function parseWorkspaceFileMentionLink(href, rawLabel) {
1497
+ const label = rawLabel.trim();
1498
+ const target = parseWorkspaceFileMentionTarget(href);
1499
+ if (!target || !label.startsWith("@")) {
1500
+ return null;
1501
+ }
1502
+ const fileLabel = label.replace(/^@+/, "").trim();
1503
+ if (!fileLabel) {
1504
+ return null;
1505
+ }
1506
+ const entryKind = resolveWorkspaceFileMentionEntryKind(
1507
+ target.path,
1508
+ fileLabel,
1509
+ target.explicitKind
1510
+ );
1511
+ return {
1512
+ label: fileLabel,
1513
+ href: target.path,
1514
+ entryKind,
1515
+ visualKind: resolveAgentWorkspaceFileVisualKind(target.path, {
1516
+ refType: entryKind === "directory" ? "folder" : "file"
1517
+ })
1518
+ };
1519
+ }
1520
+ function WorkspaceFileMentionLink({
1521
+ onClick: _onClick,
1522
+ onLinkClick,
1523
+ href: _href,
1524
+ mention,
1525
+ ...props
1526
+ }) {
1527
+ "use memo";
1528
+ return /* @__PURE__ */ jsxs2(
1529
+ "a",
1530
+ {
1531
+ ...props,
1532
+ className: cn(
1533
+ "tsh-workspace-file-link tsh-agent-object-token tsh-agent-object-token--file",
1534
+ props.className
1535
+ ),
1536
+ "data-agent-file-mention": "true",
1537
+ "data-agent-mention-kind": "file",
1538
+ "data-agent-file-entry-kind": mention.entryKind,
1539
+ "data-agent-file-visual-kind": mention.visualKind,
1540
+ "data-agent-link-href": mention.href,
1541
+ "data-agent-mention-href": mention.href,
1542
+ "aria-label": mention.label,
1543
+ role: "link",
1544
+ tabIndex: 0,
1545
+ onClick: (event) => {
1546
+ activateMarkdownLink(event, mention.href, onLinkClick);
1547
+ },
1548
+ onPointerDown: (event) => {
1549
+ activateMarkdownLinkFromPointer(event, mention.href, onLinkClick);
1550
+ },
1551
+ onKeyDown: (event) => {
1552
+ activateMarkdownLinkFromKey(event, mention.href, onLinkClick);
1553
+ },
1554
+ children: [
1555
+ /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__icon", "aria-hidden": "true" }),
1556
+ /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__main", children: mention.label })
1557
+ ]
1558
+ }
1559
+ );
1560
+ }
1561
+ function MentionLink({
1562
+ onClick: _onClick,
1563
+ onLinkClick,
1564
+ href,
1565
+ mention,
1566
+ ...props
1567
+ }) {
1568
+ "use memo";
1569
+ return /* @__PURE__ */ jsxs2(
1570
+ "a",
1571
+ {
1572
+ ...props,
1573
+ className: cn(
1574
+ "tsh-agent-object-token tsh-agent-object-token--entity",
1575
+ props.className
1576
+ ),
1577
+ "data-agent-file-mention": "true",
1578
+ "data-agent-link-href": href,
1579
+ "data-agent-mention-icon-url": mention.iconUrl,
1580
+ "data-agent-mention-href": href,
1581
+ "data-agent-mention-kind": mention.kind,
1582
+ "aria-label": mention.label,
1583
+ role: "link",
1584
+ tabIndex: 0,
1585
+ onClick: (event) => {
1586
+ activateMarkdownLink(event, href, onLinkClick);
1587
+ },
1588
+ onPointerDown: (event) => {
1589
+ activateMarkdownLinkFromPointer(event, href, onLinkClick);
1590
+ },
1591
+ onKeyDown: (event) => {
1592
+ activateMarkdownLinkFromKey(event, href, onLinkClick);
1593
+ },
1594
+ children: [
1595
+ mention.kind === "workspace-app" ? /* @__PURE__ */ jsx4(
1596
+ "span",
1597
+ {
1598
+ className: "grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px] bg-block",
1599
+ "aria-hidden": "true",
1600
+ "data-agent-mention-app-icon": "true",
1601
+ "data-workspace-app-icon": "true",
1602
+ children: mention.iconUrl ? /* @__PURE__ */ jsx4(
1603
+ "img",
1604
+ {
1605
+ src: mention.iconUrl,
1606
+ alt: "",
1607
+ className: "h-full w-full object-cover",
1608
+ decoding: "async",
1609
+ loading: "lazy",
1610
+ draggable: false
1611
+ }
1612
+ ) : /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__kind-icon h-4 w-4" })
1613
+ }
1614
+ ) : /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__kind", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
1615
+ "span",
1616
+ {
1617
+ className: "tsh-agent-object-token__kind-icon",
1618
+ "aria-hidden": "true"
1619
+ }
1620
+ ) }),
1621
+ mention.kind === "session" ? /* @__PURE__ */ jsxs2("span", { className: "tsh-agent-object-token__main", children: [
1622
+ /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__participant", children: mention.participant }),
1623
+ mention.summary ? /* @__PURE__ */ jsxs2("span", { className: "tsh-agent-object-token__summary", children: [
1624
+ " ",
1625
+ mention.summary
1626
+ ] }) : null
1627
+ ] }) : /* @__PURE__ */ jsx4("span", { className: "tsh-agent-object-token__main", children: mention.label })
1628
+ ]
1629
+ }
1630
+ );
1631
+ }
1632
+ function MarkdownCode({
1633
+ node: _node,
1634
+ children,
1635
+ className,
1636
+ onLinkClick,
1637
+ ...props
1638
+ }) {
1639
+ "use memo";
1640
+ const text = textFromReactNode(children).trim();
1641
+ if (!className && onLinkClick && (isLocalAbsolutePath(text) || isHttpUrl(text))) {
1642
+ return /* @__PURE__ */ jsx4("code", { ...props, className, children: /* @__PURE__ */ jsx4(PathLink, { href: text, onLinkClick, children }) });
1643
+ }
1644
+ return /* @__PURE__ */ jsx4("code", { ...props, className, children });
1645
+ }
1646
+ var cachedMarkdownImages = /* @__PURE__ */ new Map();
1647
+ var CACHED_MARKDOWN_IMAGE_REVOKE_DELAY_MS = 250;
1648
+ function MarkdownImage({
1649
+ node: _node,
1650
+ src,
1651
+ alt,
1652
+ className,
1653
+ enableZoom = false,
1654
+ ...props
1655
+ }) {
1656
+ "use memo";
1657
+ const { t } = useTranslation();
1658
+ const isInsideLink = useContext3(MarkdownLinkContext);
1659
+ const agentHostApi = useOptionalAgentHostApi() ?? getOptionalAgentHostApi();
1660
+ const workspacePath = typeof src === "string" && isLocalAbsolutePath(src) ? src.trim() : null;
1661
+ const readWorkspaceImage = workspacePath ? agentHostApi?.workspace?.readFile : void 0;
1662
+ const canReadWorkspaceImage = Boolean(workspacePath && readWorkspaceImage);
1663
+ const shouldEnableZoom = enableZoom && !isInsideLink;
1664
+ const resolvedSrc = typeof src === "string" ? resolveRenderableMarkdownImageSrc(src) : src;
1665
+ const [state, setState] = useState2(
1666
+ () => canReadWorkspaceImage && workspacePath ? peekCachedMarkdownImageState(workspacePath) ?? { status: "loading" } : null
1667
+ );
1668
+ useEffect2(() => {
1669
+ if (!workspacePath || !readWorkspaceImage) {
1670
+ setState(null);
1671
+ return;
1672
+ }
1673
+ const resolvedWorkspacePath = workspacePath;
1674
+ const resolvedReadWorkspaceImage = readWorkspaceImage;
1675
+ const cachedSrc = retainCachedMarkdownImage(resolvedWorkspacePath);
1676
+ if (cachedSrc) {
1677
+ setState({ status: "ready", src: cachedSrc });
1678
+ return () => {
1679
+ releaseCachedMarkdownImage(resolvedWorkspacePath, cachedSrc);
1680
+ };
1681
+ }
1682
+ const resolvedMimeType = resolveWorkspaceImageMimeType(
1683
+ resolvedWorkspacePath
1684
+ );
1685
+ if (!resolvedMimeType) {
1686
+ setState({
1687
+ status: "error",
1688
+ reason: "unsupported"
1689
+ });
1690
+ return;
1691
+ }
1692
+ const imageMimeType = resolvedMimeType;
1693
+ let canceled = false;
1694
+ let objectUrl = null;
1695
+ setState({ status: "loading" });
1696
+ async function loadWorkspaceImage() {
1697
+ try {
1698
+ const result = await resolvedReadWorkspaceImage({
1699
+ path: resolvedWorkspacePath
1700
+ });
1701
+ if (canceled) {
1702
+ return;
1703
+ }
1704
+ const bytes = result.bytes instanceof Uint8Array ? result.bytes : new Uint8Array(result.bytes);
1705
+ const arrayBuffer = bytes.buffer.slice(
1706
+ bytes.byteOffset,
1707
+ bytes.byteOffset + bytes.byteLength
1708
+ );
1709
+ objectUrl = cacheMarkdownImage(
1710
+ resolvedWorkspacePath,
1711
+ new Blob([arrayBuffer], { type: imageMimeType })
1712
+ );
1713
+ setState({ status: "ready", src: objectUrl });
1714
+ } catch (error) {
1715
+ if (!canceled) {
1716
+ setState({
1717
+ status: "error",
1718
+ reason: "read-failed",
1719
+ detail: error instanceof Error ? error.message : String(error)
1720
+ });
1721
+ }
1722
+ }
1723
+ }
1724
+ void loadWorkspaceImage();
1725
+ return () => {
1726
+ canceled = true;
1727
+ if (objectUrl) {
1728
+ releaseCachedMarkdownImage(resolvedWorkspacePath, objectUrl);
1729
+ }
1730
+ };
1731
+ }, [canReadWorkspaceImage, workspacePath]);
1732
+ if (!workspacePath || !readWorkspaceImage) {
1733
+ if (!shouldEnableZoom) {
1734
+ return /* @__PURE__ */ jsx4("img", { ...props, src: resolvedSrc, alt, className });
1735
+ }
1736
+ return /* @__PURE__ */ jsx4(
1737
+ ZoomableImage,
1738
+ {
1739
+ ...props,
1740
+ src: resolvedSrc,
1741
+ alt,
1742
+ className,
1743
+ wrapElement: "span"
1744
+ }
1745
+ );
1746
+ }
1747
+ if (state?.status === "ready") {
1748
+ if (!shouldEnableZoom) {
1749
+ return /* @__PURE__ */ jsx4(
1750
+ "img",
1751
+ {
1752
+ ...props,
1753
+ src: state.src,
1754
+ alt,
1755
+ className: cn(
1756
+ "block max-h-[360px] max-w-full rounded-[8px] border border-[var(--line-2)] bg-[var(--background-panel)] object-contain",
1757
+ className
1758
+ )
1759
+ }
1760
+ );
1761
+ }
1762
+ return /* @__PURE__ */ jsx4(
1763
+ ZoomableImage,
1764
+ {
1765
+ ...props,
1766
+ src: state.src,
1767
+ alt,
1768
+ className: cn(
1769
+ "block max-h-[360px] max-w-full rounded-[8px] border border-[var(--line-2)] bg-[var(--background-panel)] object-contain",
1770
+ className
1771
+ ),
1772
+ wrapElement: "span"
1773
+ }
1774
+ );
1775
+ }
1776
+ return /* @__PURE__ */ jsx4("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", {
1777
+ message: state.detail ?? ""
1778
+ }) : t("agentHost.workspaceFileManager.previewLoading") });
1779
+ }
1780
+ var MARKDOWN_ORDERED_LIST_STYLE = {
1781
+ listStylePosition: "outside",
1782
+ margin: "12px 0 8px",
1783
+ paddingInlineStart: 34,
1784
+ paddingInlineEnd: 16
1785
+ };
1786
+ var MARKDOWN_UNORDERED_LIST_STYLE = {
1787
+ margin: "12px 0 8px",
1788
+ paddingInlineStart: 0
1789
+ };
1790
+ var MARKDOWN_LIST_ITEM_STYLE = {
1791
+ margin: "4px 0"
1792
+ };
1793
+ function MarkdownUnorderedList({
1794
+ node: _node,
1795
+ className,
1796
+ style,
1797
+ ...props
1798
+ }) {
1799
+ "use memo";
1800
+ return /* @__PURE__ */ jsx4(
1801
+ "ul",
1802
+ {
1803
+ ...props,
1804
+ className: cn(
1805
+ '[&_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-[""]',
1806
+ className
1807
+ ),
1808
+ style: { ...MARKDOWN_UNORDERED_LIST_STYLE, ...style }
1809
+ }
1810
+ );
1811
+ }
1812
+ function MarkdownOrderedList({
1813
+ node: _node,
1814
+ style,
1815
+ ...props
1816
+ }) {
1817
+ "use memo";
1818
+ return /* @__PURE__ */ jsx4(
1819
+ "ol",
1820
+ {
1821
+ ...props,
1822
+ style: {
1823
+ ...MARKDOWN_ORDERED_LIST_STYLE,
1824
+ listStyleType: "decimal",
1825
+ ...style
1826
+ }
1827
+ }
1828
+ );
1829
+ }
1830
+ function MarkdownListItem({
1831
+ node: _node,
1832
+ style,
1833
+ ...props
1834
+ }) {
1835
+ "use memo";
1836
+ return /* @__PURE__ */ jsx4("li", { ...props, style: { ...MARKDOWN_LIST_ITEM_STYLE, ...style } });
1837
+ }
1838
+ function MarkdownParagraph({
1839
+ node: _node,
1840
+ inline,
1841
+ ...props
1842
+ }) {
1843
+ "use memo";
1844
+ if (inline) {
1845
+ return /* @__PURE__ */ jsx4("span", { ...props });
1846
+ }
1847
+ return /* @__PURE__ */ jsx4("p", { ...props });
1848
+ }
1849
+ function isLocalAbsolutePath(path) {
1850
+ const candidate = path.trim();
1851
+ return candidate.length > 1 && candidate.startsWith("/") && !candidate.startsWith("//") && !candidate.includes("://") && !/\s/.test(candidate);
1852
+ }
1853
+ function resolveRenderableMarkdownImageSrc(src) {
1854
+ const trimmed = src.trim();
1855
+ if (!trimmed) {
1856
+ return src;
1857
+ }
1858
+ if (!isLocalAbsolutePath(trimmed) || trimmed.startsWith("/workspace/")) {
1859
+ return src;
1860
+ }
1861
+ return new URL(trimmed, "file://").toString();
1862
+ }
1863
+ function peekCachedMarkdownImageState(path) {
1864
+ const src = cachedMarkdownImages.get(path)?.objectUrl ?? null;
1865
+ return src ? { status: "ready", src } : null;
1866
+ }
1867
+ function retainCachedMarkdownImage(path) {
1868
+ const entry = cachedMarkdownImages.get(path);
1869
+ if (!entry) {
1870
+ return null;
1871
+ }
1872
+ entry.refCount += 1;
1873
+ if (entry.revokeTimer) {
1874
+ clearTimeout(entry.revokeTimer);
1875
+ entry.revokeTimer = null;
1876
+ }
1877
+ return entry.objectUrl;
1878
+ }
1879
+ function cacheMarkdownImage(path, blob) {
1880
+ const entry = cachedMarkdownImages.get(path);
1881
+ if (entry) {
1882
+ entry.refCount += 1;
1883
+ if (entry.revokeTimer) {
1884
+ clearTimeout(entry.revokeTimer);
1885
+ entry.revokeTimer = null;
1886
+ }
1887
+ return entry.objectUrl;
1888
+ }
1889
+ const objectUrl = URL.createObjectURL(blob);
1890
+ cachedMarkdownImages.set(path, {
1891
+ objectUrl,
1892
+ refCount: 1,
1893
+ revokeTimer: null
1894
+ });
1895
+ return objectUrl;
1896
+ }
1897
+ function releaseCachedMarkdownImage(path, objectUrl) {
1898
+ const entry = cachedMarkdownImages.get(path);
1899
+ if (!entry || entry.objectUrl !== objectUrl) {
1900
+ URL.revokeObjectURL(objectUrl);
1901
+ return;
1902
+ }
1903
+ entry.refCount = Math.max(0, entry.refCount - 1);
1904
+ if (entry.refCount > 0 || entry.revokeTimer) {
1905
+ return;
1906
+ }
1907
+ entry.revokeTimer = setTimeout(() => {
1908
+ const current = cachedMarkdownImages.get(path);
1909
+ if (!current || current.objectUrl !== objectUrl || current.refCount > 0) {
1910
+ return;
1911
+ }
1912
+ cachedMarkdownImages.delete(path);
1913
+ URL.revokeObjectURL(objectUrl);
1914
+ }, CACHED_MARKDOWN_IMAGE_REVOKE_DELAY_MS);
1915
+ }
1916
+ function isHttpUrl(value) {
1917
+ const candidate = value.trim();
1918
+ if (!candidate || /\s/.test(candidate)) {
1919
+ return false;
1920
+ }
1921
+ try {
1922
+ const url = new URL(candidate);
1923
+ return url.protocol === "http:" || url.protocol === "https:";
1924
+ } catch {
1925
+ return false;
1926
+ }
1927
+ }
1928
+ function linkBareLocalAbsolutePaths(content) {
1929
+ let out = "";
1930
+ for (let index = 0; index < content.length; ) {
1931
+ const markdownLinkEnd = markdownLinkEndIndex(content, index);
1932
+ if (markdownLinkEnd > index) {
1933
+ out += content.slice(index, markdownLinkEnd);
1934
+ index = markdownLinkEnd;
1935
+ continue;
1936
+ }
1937
+ const codeSpanEnd = codeSpanEndIndex(content, index);
1938
+ if (codeSpanEnd > index) {
1939
+ out += content.slice(index, codeSpanEnd);
1940
+ index = codeSpanEnd;
1941
+ continue;
1942
+ }
1943
+ if (isLocalPathStart(content, index)) {
1944
+ const end = bareLocalPathEndIndex(content, index);
1945
+ const rawPath = trimTrailingPathPunctuation(content.slice(index, end));
1946
+ const trailing = content.slice(index + rawPath.length, end);
1947
+ if (isLocalAbsolutePath(rawPath)) {
1948
+ out += `[${escapeMarkdownLinkLabel(rawPath)}](${rawPath})${trailing}`;
1949
+ } else {
1950
+ out += content.slice(index, end);
1951
+ }
1952
+ index = end;
1953
+ continue;
1954
+ }
1955
+ out += content[index];
1956
+ index += 1;
1957
+ }
1958
+ return out;
1959
+ }
1960
+ function normalizePlainIssueMentionTitleContent(content) {
1961
+ const trimmed = content.trim();
1962
+ if (trimmed !== content || !trimmed.startsWith("@") || trimmed.includes("\n") || markdownLinkEndIndex(trimmed, 0) === trimmed.length) {
1963
+ return content;
1964
+ }
1965
+ const label = trimmed.replace(/^@+/, "").trim();
1966
+ if (!label) {
1967
+ return content;
1968
+ }
1969
+ return `[@${escapeMarkdownLinkLabel(label)}](mention://workspace-issue?source=plain-title)`;
1970
+ }
1971
+ function normalizeMentionMarkdownLinks(content) {
1972
+ return content.replace(/\]([\t ]*\r?\n[\t ]*)+\((mention:\/\/)/g, "]($2").replace(/\]\((mention:\/\/[A-Za-z0-9.-]+)\)\?([^\s)]+)/g, "]($1?$2)");
1973
+ }
1974
+ function isMentionOnlyMarkdownContent(content) {
1975
+ const trimmed = content.trim();
1976
+ if (trimmed.length === 0) {
1977
+ return false;
1978
+ }
1979
+ if (markdownLinkEndIndex(trimmed, 0) !== trimmed.length) {
1980
+ return false;
1981
+ }
1982
+ const labelEnd = trimmed.indexOf("]");
1983
+ return trimmed.slice(labelEnd + 2).startsWith("mention://");
1984
+ }
1985
+ function normalizePlainSessionMentionTitle(content) {
1986
+ const trimmed = content.trim();
1987
+ if (trimmed !== content || !trimmed.startsWith("@") || trimmed.includes("\n")) {
1988
+ return content;
1989
+ }
1990
+ for (const agentLabel of PLAIN_SESSION_MENTION_AGENT_LABELS) {
1991
+ const separator = ` & ${agentLabel}`;
1992
+ const separatorIndex = trimmed.indexOf(separator);
1993
+ if (separatorIndex <= 1) {
1994
+ continue;
1995
+ }
1996
+ const userLabel = trimmed.slice(1, separatorIndex).trim();
1997
+ const summary = trimmed.slice(separatorIndex + separator.length).trim();
1998
+ if (!userLabel) {
1999
+ continue;
2000
+ }
2001
+ const mentionLabel = [userLabel, agentLabel, summary].filter(Boolean).join(" \xB7 ");
2002
+ return `[@${escapeMarkdownLinkLabel(mentionLabel)}](mention://agent-session?source=plain-title)`;
2003
+ }
2004
+ return content;
2005
+ }
2006
+ function markdownUrlTransform(value) {
2007
+ return value.startsWith("mention://") ? value : defaultUrlTransform(value);
2008
+ }
2009
+ function parseMentionLink(href, rawLabel, workspaceAppIcons = [], appFactoryFallbackLabel = "Create app") {
2010
+ let url;
2011
+ try {
2012
+ url = new URL(href);
2013
+ } catch {
2014
+ return null;
2015
+ }
2016
+ if (url.protocol !== "mention:") {
2017
+ return null;
2018
+ }
2019
+ const resource = url.hostname.trim().toLowerCase();
2020
+ const kind = resource === "agent-session" ? "session" : resource === "workspace-app" ? "workspace-app" : resource === "workspace-app-factory" ? "workspace-app-factory" : resource === "workspace-issue" ? "workspace-issue" : resource;
2021
+ if (kind !== "session" && kind !== "workspace-app" && kind !== "workspace-app-factory" && kind !== "workspace-issue") {
2022
+ return null;
2023
+ }
2024
+ const label = rawLabel.trim().replace(/^@+/, "").trim() || (kind === "workspace-app-factory" ? appFactoryFallbackLabel : "");
2025
+ if (kind === "workspace-app" || kind === "workspace-app-factory") {
2026
+ const appId = url.searchParams.get("appId")?.trim() || "";
2027
+ const workspaceId = url.searchParams.get("workspaceId")?.trim() || "";
2028
+ return {
2029
+ kind,
2030
+ ...kind === "workspace-app" ? { appId } : {},
2031
+ label,
2032
+ ...kind === "workspace-app" ? {
2033
+ iconUrl: resolveWorkspaceAppMentionIconUrl({
2034
+ appId,
2035
+ workspaceAppIcons,
2036
+ workspaceId
2037
+ })
2038
+ } : {},
2039
+ participant: label,
2040
+ summary: ""
2041
+ };
2042
+ }
2043
+ if (kind === "workspace-issue") {
2044
+ return {
2045
+ kind,
2046
+ label,
2047
+ participant: label,
2048
+ summary: ""
2049
+ };
2050
+ }
2051
+ const sessionLabel = parseSessionMentionLabel(label);
2052
+ return {
2053
+ kind,
2054
+ label,
2055
+ participant: sessionLabel.participant,
2056
+ summary: sessionLabel.summary
2057
+ };
2058
+ }
2059
+ function resolveWorkspaceAppMentionIconUrl(input) {
2060
+ const appId = input.appId.trim();
2061
+ if (!appId) {
2062
+ return void 0;
2063
+ }
2064
+ const workspaceId = input.workspaceId.trim();
2065
+ const exactMatch = input.workspaceAppIcons.find(
2066
+ (icon) => icon.appId.trim() === appId && (icon.workspaceId?.trim() ?? "") === workspaceId && icon.iconUrl?.trim()
2067
+ );
2068
+ const fallbackMatch = input.workspaceAppIcons.find(
2069
+ (icon) => icon.appId.trim() === appId && icon.iconUrl?.trim()
2070
+ );
2071
+ return exactMatch?.iconUrl?.trim() || fallbackMatch?.iconUrl?.trim() || void 0;
2072
+ }
2073
+ function parseSessionMentionLabel(label) {
2074
+ const dottedParts = label.split("\xB7").map((part) => part.trim()).filter(Boolean);
2075
+ if (dottedParts.length >= 3) {
2076
+ return {
2077
+ participant: `${dottedParts[0]} & ${dottedParts[1]}`,
2078
+ summary: dottedParts.slice(2).join(" ")
2079
+ };
2080
+ }
2081
+ return {
2082
+ participant: label,
2083
+ summary: ""
2084
+ };
2085
+ }
2086
+ function markdownLinkEndIndex(content, index) {
2087
+ if (content[index] !== "[") {
2088
+ return -1;
2089
+ }
2090
+ const labelEnd = content.indexOf("]", index + 1);
2091
+ if (labelEnd < 0 || content[labelEnd + 1] !== "(") {
2092
+ return -1;
2093
+ }
2094
+ const hrefEnd = content.indexOf(")", labelEnd + 2);
2095
+ return hrefEnd < 0 ? -1 : hrefEnd + 1;
2096
+ }
2097
+ function codeSpanEndIndex(content, index) {
2098
+ if (content[index] !== "`") {
2099
+ return -1;
2100
+ }
2101
+ let tickCount = 1;
2102
+ while (content[index + tickCount] === "`") {
2103
+ tickCount += 1;
2104
+ }
2105
+ const fence = "`".repeat(tickCount);
2106
+ const end = content.indexOf(fence, index + tickCount);
2107
+ return end < 0 ? -1 : end + tickCount;
2108
+ }
2109
+ function isLocalPathStart(content, index) {
2110
+ if (content[index] !== "/" || content[index + 1] === "/") {
2111
+ return false;
2112
+ }
2113
+ const previous = content[index - 1];
2114
+ return previous === void 0 || /\s/.test(previous) || previous === "(" || previous === "[";
2115
+ }
2116
+ function bareLocalPathEndIndex(content, index) {
2117
+ let end = index;
2118
+ while (end < content.length) {
2119
+ const char = content[end];
2120
+ if (!char || /[\s<>[\](){}"'`]/.test(char)) {
2121
+ break;
2122
+ }
2123
+ end += 1;
2124
+ }
2125
+ return end;
2126
+ }
2127
+ function trimTrailingPathPunctuation(path) {
2128
+ return path.replace(/[.,;:!?,。;:!?]+$/g, "");
2129
+ }
2130
+ function escapeMarkdownLinkLabel(label) {
2131
+ return label.replace(/([\\[\]])/g, "\\$1");
2132
+ }
2133
+ function PathLink({
2134
+ href,
2135
+ children,
2136
+ onLinkClick
2137
+ }) {
2138
+ "use memo";
2139
+ return /* @__PURE__ */ jsx4(
2140
+ "a",
2141
+ {
2142
+ className: "cursor-pointer",
2143
+ "data-agent-link-href": href,
2144
+ role: "link",
2145
+ tabIndex: 0,
2146
+ onClick: (event) => {
2147
+ activateMarkdownLink(event, href, onLinkClick);
2148
+ },
2149
+ onPointerDown: (event) => {
2150
+ activateMarkdownLinkFromPointer(event, href, onLinkClick);
2151
+ },
2152
+ onKeyDown: (event) => {
2153
+ activateMarkdownLinkFromKey(event, href, onLinkClick);
2154
+ },
2155
+ children
2156
+ }
2157
+ );
2158
+ }
2159
+ function textFromReactNode(node) {
2160
+ if (typeof node === "string" || typeof node === "number") {
2161
+ return String(node);
2162
+ }
2163
+ if (Array.isArray(node)) {
2164
+ return node.map(textFromReactNode).join("");
2165
+ }
2166
+ return "";
2167
+ }
2168
+
2169
+ // agent-gui/agentGuiNode/AgentGUIConversation.styles.ts
2170
+ var styles = {
2171
+ interactivePrompt: "agent-gui-conversation__interactive-prompt",
2172
+ interactivePromptCard: "agent-gui-conversation__interactive-prompt-card",
2173
+ interactivePromptHeader: "agent-gui-conversation__interactive-prompt-header",
2174
+ interactivePromptLead: "agent-gui-conversation__interactive-prompt-lead",
2175
+ interactivePromptMeta: "agent-gui-conversation__interactive-prompt-meta",
2176
+ interactivePromptQuestion: "agent-gui-conversation__interactive-prompt-question",
2177
+ interactivePromptOptions: "agent-gui-conversation__interactive-prompt-options",
2178
+ interactiveOptionDisplay: "agent-gui-conversation__interactive-option-display",
2179
+ interactiveOptionButton: "agent-gui-conversation__interactive-option-button",
2180
+ interactiveOptionTitle: "agent-gui-conversation__interactive-option-title",
2181
+ interactiveOptionDescription: "agent-gui-conversation__interactive-option-description",
2182
+ interactiveOptionCommandDescription: "agent-gui-conversation__interactive-option-command-description",
2183
+ interactiveOptionCommandTooltip: "agent-gui-conversation__interactive-option-command-tooltip",
2184
+ interactiveOptionShortcut: "agent-gui-conversation__interactive-option-shortcut",
2185
+ interactiveOptionSpinner: "agent-gui-conversation__interactive-option-spinner",
2186
+ interactiveFeedbackComposer: "agent-gui-conversation__interactive-feedback-composer",
2187
+ interactivePromptTextarea: "agent-gui-conversation__interactive-prompt-textarea",
2188
+ interactiveFeedbackSendButton: "agent-gui-conversation__interactive-feedback-send-button",
2189
+ interactivePromptFooter: "agent-gui-conversation__interactive-prompt-footer",
2190
+ interactivePromptActions: "agent-gui-conversation__interactive-prompt-actions",
2191
+ userMessageFlow: "agent-gui-conversation__user-message-flow",
2192
+ assistantMessageFlow: "agent-gui-conversation__assistant-message-flow",
2193
+ messageGroup: "agent-gui-conversation__message-group",
2194
+ messageCopyButton: "agent-gui-conversation__message-copy-button",
2195
+ userMessageBubble: "agent-gui-conversation__user-message-bubble",
2196
+ assistantMarkdown: "agent-gui-conversation__assistant-markdown"
2197
+ };
2198
+ var AgentGUIConversation_styles_default = styles;
2199
+
2200
+ // app/renderer/components/icons/MessageSquareMoreIcon.tsx
2201
+ import {
2202
+ forwardRef,
2203
+ useCallback as useCallback2,
2204
+ useEffect as useEffect3,
2205
+ useImperativeHandle,
2206
+ useRef as useRef2
2207
+ } from "react";
2208
+ import {
2209
+ motion,
2210
+ useAnimation,
2211
+ useReducedMotion
2212
+ } from "framer-motion";
2213
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
2214
+ var DOT_TRANSITION = {
2215
+ times: [0, 0.1, 0.1, 0.2, 0.5, 0.6, 0.6, 0.7],
2216
+ duration: 1.5
2217
+ };
2218
+ var DOT_VARIANTS = {
2219
+ normal: {
2220
+ opacity: 1
2221
+ },
2222
+ animate: (custom) => ({
2223
+ opacity: [1, 0, 0, 1, 1, 0, 0, 1],
2224
+ transition: {
2225
+ opacity: {
2226
+ ...DOT_TRANSITION,
2227
+ times: DOT_TRANSITION.times.map(
2228
+ (time, index) => index === 2 || index === 3 || index === 6 || index === 7 ? time + custom * 0.1 : time
2229
+ )
2230
+ }
2231
+ }
2232
+ }),
2233
+ active: (custom) => ({
2234
+ opacity: [1, 0, 0, 1, 1, 0, 0, 1],
2235
+ transition: {
2236
+ opacity: {
2237
+ ...DOT_TRANSITION,
2238
+ repeat: Infinity,
2239
+ times: DOT_TRANSITION.times.map(
2240
+ (time, index) => index === 2 || index === 3 || index === 6 || index === 7 ? time + custom * 0.1 : time
2241
+ )
2242
+ }
2243
+ }
2244
+ })
2245
+ };
2246
+ var MessageSquareMoreIcon = forwardRef(
2247
+ ({
2248
+ active = false,
2249
+ onMouseEnter,
2250
+ onMouseLeave,
2251
+ className,
2252
+ size = 28,
2253
+ ...props
2254
+ }, ref) => {
2255
+ const controls = useAnimation();
2256
+ const reduceMotion = useReducedMotion();
2257
+ const isControlledRef = useRef2(false);
2258
+ const startAnimation = useCallback2(() => {
2259
+ if (reduceMotion) {
2260
+ return;
2261
+ }
2262
+ void controls.start(active ? "active" : "animate");
2263
+ }, [active, controls, reduceMotion]);
2264
+ const stopAnimation = useCallback2(() => {
2265
+ void controls.start("normal");
2266
+ }, [controls]);
2267
+ useImperativeHandle(ref, () => {
2268
+ isControlledRef.current = true;
2269
+ return {
2270
+ startAnimation,
2271
+ stopAnimation
2272
+ };
2273
+ });
2274
+ useEffect3(() => {
2275
+ if (active) {
2276
+ startAnimation();
2277
+ return;
2278
+ }
2279
+ stopAnimation();
2280
+ }, [active, startAnimation, stopAnimation]);
2281
+ const handleMouseEnter = useCallback2(
2282
+ (event) => {
2283
+ if (isControlledRef.current) {
2284
+ onMouseEnter?.(event);
2285
+ } else {
2286
+ startAnimation();
2287
+ }
2288
+ },
2289
+ [onMouseEnter, startAnimation]
2290
+ );
2291
+ const handleMouseLeave = useCallback2(
2292
+ (event) => {
2293
+ if (isControlledRef.current) {
2294
+ onMouseLeave?.(event);
2295
+ } else {
2296
+ stopAnimation();
2297
+ }
2298
+ },
2299
+ [onMouseLeave, stopAnimation]
2300
+ );
2301
+ return /* @__PURE__ */ jsx5(
2302
+ "div",
2303
+ {
2304
+ className: cn("inline-flex items-center justify-center", className),
2305
+ onMouseEnter: handleMouseEnter,
2306
+ onMouseLeave: handleMouseLeave,
2307
+ ...props,
2308
+ children: /* @__PURE__ */ jsxs3(
2309
+ "svg",
2310
+ {
2311
+ fill: "none",
2312
+ height: size,
2313
+ stroke: "currentColor",
2314
+ strokeLinecap: "round",
2315
+ strokeLinejoin: "round",
2316
+ strokeWidth: "2",
2317
+ viewBox: "0 0 24 24",
2318
+ width: size,
2319
+ xmlns: "http://www.w3.org/2000/svg",
2320
+ children: [
2321
+ /* @__PURE__ */ jsx5("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }),
2322
+ /* @__PURE__ */ jsx5(
2323
+ motion.path,
2324
+ {
2325
+ animate: controls,
2326
+ custom: 0,
2327
+ d: "M8 10h.01",
2328
+ variants: DOT_VARIANTS
2329
+ }
2330
+ ),
2331
+ /* @__PURE__ */ jsx5(
2332
+ motion.path,
2333
+ {
2334
+ animate: controls,
2335
+ custom: 1,
2336
+ d: "M12 10h.01",
2337
+ variants: DOT_VARIANTS
2338
+ }
2339
+ ),
2340
+ /* @__PURE__ */ jsx5(
2341
+ motion.path,
2342
+ {
2343
+ animate: controls,
2344
+ custom: 2,
2345
+ d: "M16 10h.01",
2346
+ variants: DOT_VARIANTS
2347
+ }
2348
+ )
2349
+ ]
2350
+ }
2351
+ )
2352
+ }
2353
+ );
2354
+ }
2355
+ );
2356
+ MessageSquareMoreIcon.displayName = "MessageSquareMoreIcon";
2357
+
2358
+ // app/renderer/components/ui/custom-scroll-area.tsx
2359
+ import {
2360
+ forwardRef as forwardRef2,
2361
+ useCallback as useCallback3,
2362
+ useEffect as useEffect4,
2363
+ useRef as useRef3,
2364
+ useState as useState3
2365
+ } from "react";
2366
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2367
+ var MIN_THUMB_HEIGHT = 24;
2368
+ function CustomScrollbar({
2369
+ getViewport,
2370
+ className,
2371
+ thumbClassName,
2372
+ testId,
2373
+ thumbTestId,
2374
+ syncKey
2375
+ }) {
2376
+ "use memo";
2377
+ const trackRef = useRef3(null);
2378
+ const dragStateRef = useRef3(null);
2379
+ const [scrollbarState, setScrollbarState] = useState3({
2380
+ scrollable: false,
2381
+ thumbHeight: 0,
2382
+ thumbTop: 0
2383
+ });
2384
+ const [dragging, setDragging] = useState3(false);
2385
+ const syncScrollbarState = useCallback3(() => {
2386
+ const viewport = getViewport();
2387
+ if (!viewport) {
2388
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2389
+ return;
2390
+ }
2391
+ const { clientHeight, scrollHeight, scrollTop } = viewport;
2392
+ const trackHeight = trackRef.current?.clientHeight ?? clientHeight;
2393
+ const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
2394
+ if (clientHeight <= 0 || trackHeight <= 0 || maxScrollTop <= 0) {
2395
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2396
+ return;
2397
+ }
2398
+ const thumbHeight = Math.max(
2399
+ MIN_THUMB_HEIGHT,
2400
+ Math.round(clientHeight / scrollHeight * trackHeight)
2401
+ );
2402
+ const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
2403
+ const thumbTop = Math.round(scrollTop / maxScrollTop * maxThumbTop);
2404
+ setScrollbarState(
2405
+ (previous) => previous.scrollable && previous.thumbHeight === thumbHeight && previous.thumbTop === thumbTop ? previous : { scrollable: true, thumbHeight, thumbTop }
2406
+ );
2407
+ }, [getViewport]);
2408
+ const scrollViewportToThumbTop = useCallback3(
2409
+ (thumbTop) => {
2410
+ const viewport = getViewport();
2411
+ const track = trackRef.current;
2412
+ if (!viewport || !track) {
2413
+ return;
2414
+ }
2415
+ const maxScrollTop = Math.max(
2416
+ 0,
2417
+ viewport.scrollHeight - viewport.clientHeight
2418
+ );
2419
+ const maxThumbTop = Math.max(
2420
+ 0,
2421
+ track.clientHeight - scrollbarState.thumbHeight
2422
+ );
2423
+ if (maxScrollTop <= 0 || maxThumbTop <= 0) {
2424
+ return;
2425
+ }
2426
+ viewport.scrollTop = clamp(thumbTop, 0, maxThumbTop) / maxThumbTop * maxScrollTop;
2427
+ syncScrollbarState();
2428
+ },
2429
+ [getViewport, scrollbarState.thumbHeight, syncScrollbarState]
2430
+ );
2431
+ const handleTrackMouseDown = useCallback3(
2432
+ (event) => {
2433
+ if (event.button !== 0 || !scrollbarState.scrollable) {
2434
+ return;
2435
+ }
2436
+ const track = trackRef.current;
2437
+ if (!track) {
2438
+ return;
2439
+ }
2440
+ event.preventDefault();
2441
+ event.stopPropagation();
2442
+ const trackRect = track.getBoundingClientRect();
2443
+ scrollViewportToThumbTop(
2444
+ event.clientY - trackRect.top - scrollbarState.thumbHeight / 2
2445
+ );
2446
+ },
2447
+ [
2448
+ scrollViewportToThumbTop,
2449
+ scrollbarState.scrollable,
2450
+ scrollbarState.thumbHeight
2451
+ ]
2452
+ );
2453
+ const handleThumbMouseDown = useCallback3(
2454
+ (event) => {
2455
+ if (event.button !== 0 || !scrollbarState.scrollable) {
2456
+ return;
2457
+ }
2458
+ const viewport = getViewport();
2459
+ const track = trackRef.current;
2460
+ if (!viewport || !track) {
2461
+ return;
2462
+ }
2463
+ const maxScrollTop = Math.max(
2464
+ 0,
2465
+ viewport.scrollHeight - viewport.clientHeight
2466
+ );
2467
+ const maxThumbTop = Math.max(
2468
+ 0,
2469
+ track.clientHeight - scrollbarState.thumbHeight
2470
+ );
2471
+ if (maxScrollTop <= 0 || maxThumbTop <= 0) {
2472
+ return;
2473
+ }
2474
+ event.preventDefault();
2475
+ event.stopPropagation();
2476
+ dragStateRef.current = {
2477
+ maxScrollTop,
2478
+ maxThumbTop,
2479
+ startClientY: event.clientY,
2480
+ startScrollTop: viewport.scrollTop
2481
+ };
2482
+ setDragging(true);
2483
+ },
2484
+ [getViewport, scrollbarState.scrollable, scrollbarState.thumbHeight]
2485
+ );
2486
+ useEffect4(() => {
2487
+ if (!dragging) {
2488
+ return;
2489
+ }
2490
+ const handleMouseMove = (event) => {
2491
+ const dragState = dragStateRef.current;
2492
+ const viewport = getViewport();
2493
+ if (!dragState || !viewport) {
2494
+ return;
2495
+ }
2496
+ const nextThumbTop = dragState.startScrollTop / dragState.maxScrollTop * dragState.maxThumbTop + (event.clientY - dragState.startClientY);
2497
+ viewport.scrollTop = clamp(nextThumbTop, 0, dragState.maxThumbTop) / dragState.maxThumbTop * dragState.maxScrollTop;
2498
+ syncScrollbarState();
2499
+ };
2500
+ const handleMouseUp = () => {
2501
+ dragStateRef.current = null;
2502
+ setDragging(false);
2503
+ };
2504
+ window.addEventListener("mousemove", handleMouseMove);
2505
+ window.addEventListener("mouseup", handleMouseUp);
2506
+ return () => {
2507
+ window.removeEventListener("mousemove", handleMouseMove);
2508
+ window.removeEventListener("mouseup", handleMouseUp);
2509
+ };
2510
+ }, [dragging, getViewport, syncScrollbarState]);
2511
+ useEffect4(() => {
2512
+ const viewport = getViewport();
2513
+ if (!viewport) {
2514
+ setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
2515
+ return;
2516
+ }
2517
+ syncScrollbarState();
2518
+ viewport.addEventListener("scroll", syncScrollbarState, { passive: true });
2519
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(syncScrollbarState) : null;
2520
+ resizeObserver?.observe(viewport);
2521
+ const animationFrameId = window.requestAnimationFrame(syncScrollbarState);
2522
+ return () => {
2523
+ window.cancelAnimationFrame(animationFrameId);
2524
+ viewport.removeEventListener("scroll", syncScrollbarState);
2525
+ resizeObserver?.disconnect();
2526
+ };
2527
+ }, [getViewport, syncKey, syncScrollbarState]);
2528
+ return /* @__PURE__ */ jsx6(
2529
+ "div",
2530
+ {
2531
+ ref: trackRef,
2532
+ className: cn("tsh-custom-scrollbar", className),
2533
+ "data-scrollable": scrollbarState.scrollable ? "true" : "false",
2534
+ "data-dragging": dragging ? "true" : "false",
2535
+ "data-testid": testId,
2536
+ "aria-hidden": "true",
2537
+ onMouseDown: handleTrackMouseDown,
2538
+ children: /* @__PURE__ */ jsx6(
2539
+ "div",
2540
+ {
2541
+ className: cn("tsh-custom-scrollbar__thumb", thumbClassName),
2542
+ "data-testid": thumbTestId,
2543
+ onMouseDown: handleThumbMouseDown,
2544
+ style: {
2545
+ height: `${scrollbarState.thumbHeight}px`,
2546
+ transform: `translateY(${scrollbarState.thumbTop}px)`
2547
+ }
2548
+ }
2549
+ )
2550
+ }
2551
+ );
2552
+ }
2553
+ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2554
+ children,
2555
+ className,
2556
+ viewportClassName,
2557
+ scrollbarClassName,
2558
+ scrollbarThumbClassName,
2559
+ scrollbarTestId,
2560
+ scrollbarThumbTestId,
2561
+ syncKey,
2562
+ ...viewportProps
2563
+ }, forwardedRef) {
2564
+ "use memo";
2565
+ const viewportRef = useRef3(null);
2566
+ const getViewport = useCallback3(() => viewportRef.current, []);
2567
+ return /* @__PURE__ */ jsxs4(
2568
+ "div",
2569
+ {
2570
+ className: cn(
2571
+ "tsh-custom-scroll-area relative min-h-0 min-w-0",
2572
+ className
2573
+ ),
2574
+ children: [
2575
+ /* @__PURE__ */ jsx6(
2576
+ "div",
2577
+ {
2578
+ ref: setRefs(viewportRef, forwardedRef),
2579
+ className: cn(
2580
+ "overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
2581
+ viewportClassName
2582
+ ),
2583
+ ...viewportProps,
2584
+ children
2585
+ }
2586
+ ),
2587
+ /* @__PURE__ */ jsx6(
2588
+ CustomScrollbar,
2589
+ {
2590
+ getViewport,
2591
+ className: scrollbarClassName,
2592
+ thumbClassName: scrollbarThumbClassName,
2593
+ testId: scrollbarTestId,
2594
+ thumbTestId: scrollbarThumbTestId,
2595
+ syncKey: syncKey ?? children
2596
+ }
2597
+ )
2598
+ ]
2599
+ }
2600
+ );
2601
+ });
2602
+ function setRefs(localRef, forwardedRef) {
2603
+ return (node) => {
2604
+ localRef.current = node;
2605
+ if (typeof forwardedRef === "function") {
2606
+ forwardedRef(node);
2607
+ } else if (forwardedRef) {
2608
+ forwardedRef.current = node;
2609
+ }
2610
+ };
2611
+ }
2612
+ function clamp(value, min, max) {
2613
+ return Math.min(max, Math.max(min, value));
2614
+ }
2615
+
2616
+ export {
2617
+ cn,
2618
+ AgentActivityRuntimeProvider,
2619
+ useAgentActivityRuntime,
2620
+ useOptionalAgentActivityRuntime,
2621
+ useAgentActivitySnapshot,
2622
+ getAgentActivityRuntime,
2623
+ getOptionalAgentActivityRuntime,
2624
+ resetAgentActivityRuntimeForTests,
2625
+ setAgentActivityRuntimeForTests,
2626
+ AgentActivityHostProvider,
2627
+ useAgentHostApi,
2628
+ useOptionalAgentHostApi,
2629
+ getOptionalAgentHostApi,
2630
+ extractAgentMcpToolTarget,
2631
+ normalizeAskUserQuestions,
2632
+ resolveWebsiteNavigationUrl,
2633
+ ZoomableImage,
2634
+ resolveWorkspaceLinkAction,
2635
+ AgentMessageMarkdown,
2636
+ AgentGUIConversation_styles_default,
2637
+ CustomScrollArea,
2638
+ MessageSquareMoreIcon
2639
+ };
2640
+ //# sourceMappingURL=chunk-QTZALZIV.js.map