@runfusion/fusion 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/bin.js +4077 -1291
  2. package/dist/client/assets/{AgentDetailView-D9UWpTYr.js → AgentDetailView-BtpZ4jxh.js} +3 -3
  3. package/dist/client/assets/{AgentsView-DeCfRupM.js → AgentsView-Dxdtt0Bm.js} +3 -3
  4. package/dist/client/assets/ChatView-Bra9fNAG.js +1 -0
  5. package/dist/client/assets/{DevServerView-B7EjWlgc.js → DevServerView-UkgjEw9-.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-crtmkC00.js → DirectoryPicker-Cls4HWxP.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-BLxVoopL.js → DocumentsView-BRBUPFVA.js} +1 -1
  8. package/dist/client/assets/{InsightsView-CcdTychV.js → InsightsView-BRDqHCLb.js} +1 -1
  9. package/dist/client/assets/{MemoryView-rSwx9Md8.js → MemoryView-DvTrwFnQ.js} +1 -1
  10. package/dist/client/assets/{NodesView-Bwz0cHKV.js → NodesView-C4Ffl_o0.js} +3 -3
  11. package/dist/client/assets/{PiExtensionsManager-Uo3E8Ae7.js → PiExtensionsManager-CeI1syeZ.js} +3 -3
  12. package/dist/client/assets/{PluginManager-HtW8xVY8.js → PluginManager-BgeoYhLk.js} +1 -1
  13. package/dist/client/assets/ResearchView-BVJFgfat.css +1 -0
  14. package/dist/client/assets/ResearchView-fmEOm4A2.js +1 -0
  15. package/dist/client/assets/{RoadmapsView-C2j64cbz.js → RoadmapsView-DNb4x75S.js} +2 -2
  16. package/dist/client/assets/{SettingsModal-YjpwLH2V.css → SettingsModal-9HS8MnmW.css} +1 -1
  17. package/dist/client/assets/SettingsModal-CDPDmHhd.css +1 -0
  18. package/dist/client/assets/{SettingsModal-CVd9kNk7.js → SettingsModal-CRMr4tL6.js} +1 -1
  19. package/dist/client/assets/SettingsModal-D1xq0WZm.js +31 -0
  20. package/dist/client/assets/{SetupWizardModal-Dy-vQpTm.js → SetupWizardModal-tF8B_aG_.js} +1 -1
  21. package/dist/client/assets/{SkillsView-BQwTyjxc.js → SkillsView-uyl47gSf.js} +1 -1
  22. package/dist/client/assets/{TodoView-Dce4DrzU.js → TodoView-CbzDtV53.js} +2 -2
  23. package/dist/client/assets/{folder-open-DWUflP4Q.js → folder-open-DPpmGJ-v.js} +1 -1
  24. package/dist/client/assets/{index-C3-q81dV.css → index-BqK6TvSa.css} +1 -1
  25. package/dist/client/assets/index-DyXZm9QN.js +656 -0
  26. package/dist/client/assets/{list-checks-CusZ_RMn.js → list-checks-D62pw1I8.js} +1 -1
  27. package/dist/client/assets/{star-C-NXZn1F.js → star-B8EbxNgI.js} +1 -1
  28. package/dist/client/assets/{upload-CXJ5L6L4.js → upload-CpnLno9z.js} +1 -1
  29. package/dist/client/assets/{users-YaA3x5mt.js → users-B_C_0qzA.js} +1 -1
  30. package/dist/client/index.html +2 -2
  31. package/dist/client/version.json +1 -1
  32. package/dist/extension.js +3201 -714
  33. package/dist/pi-claude-cli/index.ts +31 -5
  34. package/dist/pi-claude-cli/package.json +1 -1
  35. package/dist/pi-claude-cli/src/__tests__/process-manager.test.ts +90 -0
  36. package/dist/pi-claude-cli/src/__tests__/provider.test.ts +13 -3
  37. package/dist/pi-claude-cli/src/process-manager.ts +65 -0
  38. package/package.json +1 -1
  39. package/dist/client/assets/ChatView-ChlqnJfu.js +0 -1
  40. package/dist/client/assets/ResearchView-BV-iy9g8.js +0 -1
  41. package/dist/client/assets/ResearchView-aQkoD9QR.css +0 -1
  42. package/dist/client/assets/SettingsModal-ClnT1Lcv.js +0 -31
  43. package/dist/client/assets/SettingsModal-FfIAhzcJ.css +0 -1
  44. package/dist/client/assets/index-Bs3RZu5I.js +0 -656
@@ -9,8 +9,8 @@ import { getModels } from "@mariozechner/pi-ai";
9
9
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
10
  import { streamViaCli } from "./src/provider.js";
11
11
  import {
12
- validateCliPresence,
13
- validateCliAuth,
12
+ validateCliPresenceAsync,
13
+ validateCliAuthAsync,
14
14
  killAllProcesses,
15
15
  } from "./src/process-manager.js";
16
16
  import { createHash } from "node:crypto";
@@ -26,6 +26,31 @@ process.on("exit", killAllProcesses);
26
26
 
27
27
  const PROVIDER_ID = "pi-claude-cli";
28
28
 
29
+ /**
30
+ * Run CLI presence + auth probes at most once per process, asynchronously.
31
+ *
32
+ * The factory below is invoked on every `createFnAgent` call (the dashboard
33
+ * does this per chat message). Doing the probes synchronously with execSync
34
+ * froze the entire Node event loop for a few seconds while `claude` cold-
35
+ * started. Memoizing as a Promise + spawning the probes async means the
36
+ * factory returns immediately and other requests keep flowing; the result
37
+ * is logged once on first run and reused thereafter.
38
+ */
39
+ let cliValidationPromise: Promise<void> | undefined;
40
+
41
+ function runCliValidationOnce(): Promise<void> {
42
+ if (cliValidationPromise) return cliValidationPromise;
43
+ cliValidationPromise = (async () => {
44
+ const presence = await validateCliPresenceAsync();
45
+ if (!presence.ok) {
46
+ console.warn(`[pi-claude-cli] ${presence.error.message}`);
47
+ return;
48
+ }
49
+ await validateCliAuthAsync();
50
+ })();
51
+ return cliValidationPromise;
52
+ }
53
+
29
54
  let cachedMcpConfig: { hash: string; configPath: string } | undefined;
30
55
  const DEBUG_MCP = process.env.PI_CLAUDE_CLI_DEBUG === "1";
31
56
 
@@ -116,9 +141,10 @@ function ensureMcpConfig(
116
141
 
117
142
  export default function (pi: ExtensionAPI) {
118
143
  try {
119
- // Startup validation
120
- validateCliPresence(); // throws if CLI not on PATH
121
- validateCliAuth(); // warns if not authenticated
144
+ // Startup validation: kick off async, memoized presence + auth probes
145
+ // without blocking the factory. Failures surface via warnings; the actual
146
+ // `claude` subprocess in streamViaCli still reports hard errors on send.
147
+ void runCliValidationOnce();
122
148
 
123
149
  const catalogModels = getModels("anthropic").map((model) => ({
124
150
  id: model.id,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion/pi-claude-cli",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Fusion vendored fork: pi coding-agent extension that routes LLM calls through the Claude Code CLI. Forked from rchern/pi-claude-cli (MIT). See UPSTREAM.md.",
5
5
  "license": "MIT",
6
6
  "private": true,
@@ -47,6 +47,8 @@ import {
47
47
  captureStderr,
48
48
  validateCliPresence,
49
49
  validateCliAuth,
50
+ validateCliPresenceAsync,
51
+ validateCliAuthAsync,
50
52
  forceKillProcess,
51
53
  registerProcess,
52
54
  killAllProcesses,
@@ -407,6 +409,94 @@ describe("validateCliAuth", () => {
407
409
  });
408
410
  });
409
411
 
412
+ describe("validateCliPresenceAsync", () => {
413
+ beforeEach(() => {
414
+ vi.clearAllMocks();
415
+ });
416
+
417
+ it("resolves ok=true when claude --version exits 0", async () => {
418
+ const EventEmitter = require("node:events");
419
+ (spawn as any).mockImplementationOnce(() => {
420
+ const proc = new EventEmitter();
421
+ proc.kill = vi.fn();
422
+ setImmediate(() => proc.emit("exit", 0));
423
+ return proc;
424
+ });
425
+
426
+ const result = await validateCliPresenceAsync();
427
+ expect(result).toEqual({ ok: true });
428
+ const args = (spawn as any).mock.calls[0][1] as string[];
429
+ expect(args).toEqual(["--version"]);
430
+ });
431
+
432
+ it("resolves ok=false with install message when spawn errors", async () => {
433
+ const EventEmitter = require("node:events");
434
+ (spawn as any).mockImplementationOnce(() => {
435
+ const proc = new EventEmitter();
436
+ proc.kill = vi.fn();
437
+ setImmediate(() => proc.emit("error", new Error("ENOENT")));
438
+ return proc;
439
+ });
440
+
441
+ const result = await validateCliPresenceAsync();
442
+ expect(result.ok).toBe(false);
443
+ if (!result.ok) {
444
+ expect(result.error.message).toContain("Claude Code CLI not found");
445
+ expect(result.error.message).toContain("npm install");
446
+ }
447
+ });
448
+
449
+ it("resolves ok=false when claude --version exits non-zero", async () => {
450
+ const EventEmitter = require("node:events");
451
+ (spawn as any).mockImplementationOnce(() => {
452
+ const proc = new EventEmitter();
453
+ proc.kill = vi.fn();
454
+ setImmediate(() => proc.emit("exit", 1));
455
+ return proc;
456
+ });
457
+
458
+ const result = await validateCliPresenceAsync();
459
+ expect(result.ok).toBe(false);
460
+ });
461
+ });
462
+
463
+ describe("validateCliAuthAsync", () => {
464
+ beforeEach(() => {
465
+ vi.clearAllMocks();
466
+ });
467
+
468
+ it("resolves true when claude auth status exits 0", async () => {
469
+ const EventEmitter = require("node:events");
470
+ (spawn as any).mockImplementationOnce(() => {
471
+ const proc = new EventEmitter();
472
+ proc.kill = vi.fn();
473
+ setImmediate(() => proc.emit("exit", 0));
474
+ return proc;
475
+ });
476
+
477
+ expect(await validateCliAuthAsync()).toBe(true);
478
+ const args = (spawn as any).mock.calls[0][1] as string[];
479
+ expect(args).toEqual(["auth", "status"]);
480
+ });
481
+
482
+ it("resolves false and warns when claude auth status fails", async () => {
483
+ const EventEmitter = require("node:events");
484
+ (spawn as any).mockImplementationOnce(() => {
485
+ const proc = new EventEmitter();
486
+ proc.kill = vi.fn();
487
+ setImmediate(() => proc.emit("exit", 1));
488
+ return proc;
489
+ });
490
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
491
+
492
+ expect(await validateCliAuthAsync()).toBe(false);
493
+ expect(warnSpy).toHaveBeenCalledWith(
494
+ expect.stringContaining("not authenticated"),
495
+ );
496
+ warnSpy.mockRestore();
497
+ });
498
+ });
499
+
410
500
  describe("CLI flags", () => {
411
501
  beforeEach(() => {
412
502
  vi.clearAllMocks();
@@ -168,10 +168,10 @@ describe("provider registration (default export)", () => {
168
168
  });
169
169
  });
170
170
 
171
- describe("streamViaCli", () => {
171
+ describe("streamViaCli", { timeout: 90_000 }, () => {
172
172
  beforeEach(() => {
173
173
  vi.clearAllMocks();
174
- vi.useFakeTimers();
174
+ vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
175
175
  vi.spyOn(console, "warn").mockImplementation(() => {});
176
176
  vi.spyOn(console, "error").mockImplementation(() => {});
177
177
  delete process.env.PI_CLAUDE_CLI_DEBUG;
@@ -183,7 +183,7 @@ describe("streamViaCli", () => {
183
183
  delete process.env.PI_CLAUDE_CLI_DEBUG;
184
184
  });
185
185
 
186
- it("returns an AssistantMessageEventStream", () => {
186
+ it("returns an AssistantMessageEventStream", async () => {
187
187
  const model = mockModels[0] as any;
188
188
  const context = {
189
189
  messages: [{ role: "user", content: "Hello" }],
@@ -194,6 +194,16 @@ describe("streamViaCli", () => {
194
194
  expect(result).toBeDefined();
195
195
  expect(result.push).toBeDefined();
196
196
  expect(result.end).toBeDefined();
197
+
198
+ // Ensure the spawned process/readline lifecycle completes so fake timers
199
+ // don't leave the test hanging on the inactivity timeout.
200
+ await vi.advanceTimersByTimeAsync(0);
201
+ const proc = (spawn as any).mock.results[0].value;
202
+ proc.stdout.write(
203
+ `${JSON.stringify({ type: "result", subtype: "success", result: "ok" })}\n`,
204
+ );
205
+ proc.stdout.end();
206
+ await vi.advanceTimersByTimeAsync(0);
197
207
  });
198
208
 
199
209
  it("logs PID and spawn args when debug mode is enabled", async () => {
@@ -241,3 +241,68 @@ export function validateCliAuth(): boolean {
241
241
  return false;
242
242
  }
243
243
  }
244
+
245
+ /**
246
+ * Run a one-shot `claude <args>` and resolve to the exit code.
247
+ *
248
+ * Why: the sync execSync variants block the Node event loop for the duration
249
+ * of a Claude CLI cold start (1–3s, occasionally longer). When pi-claude-cli's
250
+ * factory is invoked from a per-request createFnAgent path (Fusion dashboard
251
+ * does this on every chat send), those sync probes freeze every other request.
252
+ * This async variant uses spawn so the loop keeps turning while the subprocess
253
+ * starts up.
254
+ */
255
+ function runClaudeProbe(args: string[], timeoutMs = 5000): Promise<number> {
256
+ return new Promise((resolve) => {
257
+ const proc = spawn("claude", args, { stdio: "ignore" });
258
+ const timer = setTimeout(() => {
259
+ try {
260
+ proc.kill("SIGKILL");
261
+ } catch {
262
+ // already dead
263
+ }
264
+ resolve(124);
265
+ }, timeoutMs);
266
+ proc.once("error", () => {
267
+ clearTimeout(timer);
268
+ resolve(127);
269
+ });
270
+ proc.once("exit", (code) => {
271
+ clearTimeout(timer);
272
+ resolve(code ?? 1);
273
+ });
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Async, non-blocking variant of validateCliPresence.
279
+ * Resolves with `{ok: true}` on success, `{ok: false, error}` on failure —
280
+ * never rejects, so callers can fire-and-forget without unhandled rejections.
281
+ */
282
+ export async function validateCliPresenceAsync(): Promise<
283
+ { ok: true } | { ok: false; error: Error }
284
+ > {
285
+ const code = await runClaudeProbe(["--version"]);
286
+ if (code === 0) return { ok: true };
287
+ return {
288
+ ok: false,
289
+ error: new Error(
290
+ "Claude Code CLI not found. Install it: npm install -g @anthropic-ai/claude-code\n" +
291
+ "Then authenticate: claude auth login",
292
+ ),
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Async, non-blocking variant of validateCliAuth.
298
+ * Returns true if authenticated. Logs a warning (does not throw) otherwise.
299
+ */
300
+ export async function validateCliAuthAsync(): Promise<boolean> {
301
+ const code = await runClaudeProbe(["auth", "status"]);
302
+ if (code === 0) return true;
303
+ console.warn(
304
+ "[pi-claude-cli] Claude CLI is not authenticated. " +
305
+ "Run 'claude auth login' to authenticate.",
306
+ );
307
+ return false;
308
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runfusion/fusion",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "description": "Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.",
6
6
  "homepage": "https://github.com/Runfusion/Fusion#readme",
@@ -1 +0,0 @@
1
- import{r as s,j as t}from"./vendor-react-K0fH_qHe.js";import{j as ze,aA as Mt,g as Tt,aB as At,a as $t,aC as Pt,aD as Rt,aE as Dt,aF as Et,aG as Ft,aH as Lt,s as _t,aI as It,aJ as zt,aK as Ot,h as Ut,aL as Ht,a7 as Vt,a8 as Bt,S as Gt,N as Xe,D as Ze,ak as Kt,aM as qt,B as Ae,a4 as et,a5 as tt,aN as Jt,aO as Qt,aP as Wt,aQ as Yt,aR as Xt,aS as Zt,aT as st,i as es,u as nt,l as ts}from"./index-Bs3RZu5I.js";import"./vendor-xterm-DzcZoU0P.js";const Ie="kb-chat-active-session";function ss(a){const r=a?.toolCalls;if(!Array.isArray(r))return;const c=r.map(l=>{if(!l||typeof l!="object")return null;const o=l,C=typeof o.toolName=="string"?o.toolName:"";if(!C)return null;const M=o.args;return{toolName:C,...M&&typeof M=="object"?{args:M}:{},isError:!!o.isError,result:o.result,status:"completed"}}).filter(l=>l!==null);return c.length>0?c:void 0}function at(a){return{id:a.id,sessionId:a.sessionId,role:a.role,content:a.content,thinkingOutput:a.thinkingOutput,toolCalls:ss(a.metadata),attachments:a.attachments,createdAt:a.createdAt}}function ns(a){const[r,c]=s.useState([]),[l,o]=s.useState(null),[C,M]=s.useState(!0),[R,k]=s.useState([]),[V,I]=s.useState(!1),[p,F]=s.useState(!1),[B,D]=s.useState(""),[E,T]=s.useState(""),[z,A]=s.useState([]),[G,H]=s.useState(""),[se,ne]=s.useState(""),[$,K]=s.useState(!0),[u,w]=s.useState(new Map),x=s.useRef(null),P=s.useRef(!1),O=s.useRef(""),Se=s.useRef(r),Q=s.useRef(l),je=s.useRef(p);Se.current=r,Q.current=l,je.current=p,s.useEffect(()=>{O.current=G},[G]);const oe=s.useRef(new Set),ae=s.useRef(0),ye=s.useRef(a);ye.current!==a&&(ye.current=a,ae.current++),s.useEffect(()=>{const d=ae.current;ze(void 0,a).then(g=>{if(ae.current!==d)return;const h=new Map;for(const j of g)h.set(j.id,j);w(h)}).catch(()=>{})},[a]);const ie=s.useCallback(async()=>{M(!0);try{const g=[...(await Mt(a)).sessions].sort((h,j)=>new Date(j.updatedAt).getTime()-new Date(h.updatedAt).getTime());c(g)}catch{}finally{M(!1)}},[a]);s.useEffect(()=>{ie()},[ie]);const X=s.useRef(()=>{});s.useEffect(()=>{if(C)return;const d=Tt(Ie,a);d&&r.find(h=>h.id===d)&&X.current(d)},[C,r,a]);const q=s.useCallback(async(d,g)=>{I(!0);try{const h=await At(d,{limit:50,...g},a),j=h.messages.map(at);g?.offset&&g.offset>0?k(Z=>[...j,...Z]):k(j),K(h.messages.length>=50)}catch{}finally{I(!1)}},[a]),re=s.useCallback((d,g)=>{x.current&&(x.current.close(),x.current=null);const h=g??r.find(j=>j.id===d);o(h||null),D(""),T(""),A([]),F(!1),K(!0),d?q(d):k([]),d?$t(Ie,d,a):Pt(Ie,a)},[r,q,a]);X.current=re;const ge=s.useCallback(async d=>{const g=await Rt(d,a);x.current&&(x.current.close(),x.current=null);const h={id:g.session.id,title:g.session.title,agentId:g.session.agentId,status:g.session.status,modelProvider:g.session.modelProvider,modelId:g.session.modelId,createdAt:g.session.createdAt,updatedAt:g.session.updatedAt};return c(j=>[h,...j]),re(h.id,h),k([]),h},[a,re]),fe=s.useCallback(async d=>{await Dt(d,{status:"archived"},a),c(g=>g.filter(h=>h.id!==d)),l?.id===d&&(o(null),k([]))},[l,a]),de=s.useCallback(async d=>{l?.id===d&&x.current&&(x.current.close(),x.current=null),await Et(d,a),c(g=>g.filter(h=>h.id!==d)),l?.id===d&&(o(null),k([]))},[l,a]),pe=s.useCallback(async()=>{!l||!$||await q(l.id,{offset:R.length})},[l,$,q,R.length]),le=s.useCallback(()=>{l&&(P.current=!0,x.current?.close(),x.current=null,Ft(l.id,a).catch(()=>{}),F(!1),D(""),T(""),A([]))},[l,a]),W=s.useCallback(()=>{O.current="",H("")},[]),Y=s.useCallback((d,g)=>{if(!l)return;if(p){O.current=d,H(d);return}P.current=!1,x.current&&(x.current.close(),x.current=null);const h=`temp-${Date.now()}`,j={id:h,sessionId:l.id,role:"user",content:d,createdAt:new Date().toISOString()};k(b=>[...b,j]),D(""),T(""),A([]),F(!0);let Z="",U="",L=[];const we={onThinking:b=>{U+=b,T(U)},onText:b=>{Z+=b,D(Z)},onToolStart:b=>{L=[...L,{toolName:b.toolName,args:b.args,isError:!1,status:"running"}],A(L)},onToolEnd:b=>{const f=[...L];for(let S=f.length-1;S>=0;S--){const N=f[S];if(N?.toolName===b.toolName&&N.status==="running"){f[S]={...N,status:"completed",isError:b.isError,result:b.result},L=f,A(f);return}}L=[...f,{toolName:b.toolName,isError:b.isError,result:b.result,status:"completed"}],A(L)},onDone:b=>{const f={id:b.messageId||`msg-${Date.now()}`,sessionId:l.id,role:"assistant",content:Z,thinkingOutput:U,toolCalls:L.length>0?L:void 0,createdAt:new Date().toISOString()};oe.current.add(f.id),k(N=>[...N,f]),D(""),T(""),A([]),F(!1),x.current=null,setTimeout(()=>{oe.current.delete(f.id)},1e3),ie();const S=O.current.trim();S&&(O.current="",H(""),Y(S))},onError:b=>{if(k(f=>f.filter(S=>S.id!==h)),D(""),T(""),A([]),F(!1),x.current=null,console.error("[useChat] Stream error:",b),!P.current){const f=O.current.trim();f&&(O.current="",H(""),Y(f))}}};x.current=Lt(l.id,d,we,g,a)},[l,p,a,ie]),be=se?r.filter(d=>d.title?.toLowerCase().includes(se.toLowerCase())||d.agentId.toLowerCase().includes(se.toLowerCase())):r;return s.useEffect(()=>{const d=ae.current,g=a?`?projectId=${encodeURIComponent(a)}`:"",h=()=>ae.current!==d,j=f=>{if(h())return;const S=JSON.parse(f.data);c(N=>N.some(m=>m.id===S.id)?N:[S,...N])},Z=f=>{if(h())return;const S=JSON.parse(f.data);c(N=>[...N.map(ce=>ce.id===S.id?S:ce)]),Q.current?.id===S.id&&o(S)},U=f=>{if(h())return;const{id:S}=JSON.parse(f.data);c(N=>N.filter(m=>m.id!==S)),Q.current?.id===S&&(o(null),k([]))},L=f=>{if(h())return;const S=JSON.parse(f.data),N=at(S);oe.current.has(N.id)||Q.current?.id===N.sessionId&&!je.current&&k(m=>m.some(ce=>ce.id===N.id)?m:[...m,N])},we=f=>{if(h())return;const{id:S}=JSON.parse(f.data);k(N=>N.filter(m=>m.id!==S))};return _t(`/api/events${g}`,{events:{"chat:session:created":j,"chat:session:updated":Z,"chat:session:deleted":U,"chat:message:added":L,"chat:message:deleted":we}})},[a]),s.useEffect(()=>()=>{x.current&&(x.current.close(),x.current=null)},[]),{sessions:r,activeSession:l,sessionsLoading:C,messages:R,messagesLoading:V,isStreaming:p,streamingText:B,streamingThinking:E,streamingToolCalls:z,pendingMessage:G,selectSession:re,createSession:ge,archiveSession:fe,deleteSession:de,sendMessage:Y,stopStreaming:le,clearPendingMessage:W,loadMoreMessages:pe,hasMoreMessages:$,searchQuery:se,setSearchQuery:ne,filteredSessions:be,refreshSessions:ie,agentsMap:u}}function it(a){const r=new Date(a),l=new Date().getTime()-r.getTime(),o=Math.floor(l/1e3),C=Math.floor(o/60),M=Math.floor(C/60),R=Math.floor(M/24);return o<60?"just now":C<60?`${C}m ago`:M<24?`${M}h ago`:R<7?`${R}d ago`:r.toLocaleDateString()}function rt(a,r){if(!a||!r)return null;const c=r.toLowerCase();if(c.includes("claude")){let o=r.replace(/^claude[- ]/i,"Claude ").replace(/sonnet[- ](\d+)[- ](\d+)/i,"Sonnet $1.$2").replace(/sonnet[- ](\d+)/i,"Sonnet $1").replace(/haiku[- ](\d+)/i,"Haiku $1").replace(/opus[- ](\d+)/i,"Opus $1").replace(/sonnet/i,"Sonnet").replace(/haiku/i,"Haiku").replace(/opus/i,"Opus").replace(/-/g," ").trim();return o=o.replace(/\s+/g," "),o.length>30?o.slice(0,30)+"…":o}if(c.includes("gpt")||c.includes("openai")){const o=r.replace(/^gpt-4-turbo$/i,"GPT-4 Turbo").replace(/^gpt-4o-mini$/i,"GPT-4o Mini").replace(/^gpt-4o$/i,"GPT-4o").replace(/^gpt-4$/i,"GPT-4").replace(/^gpt-o1-preview$/i,"GPT-o1 Preview").replace(/^gpt-o1-mini$/i,"GPT-o1 Mini").replace(/^gpt-o1$/i,"GPT-o1").replace(/^gpt/i,"GPT").trim();return o.length>30?o.slice(0,30)+"…":o}if(c.includes("gemini")){const o=r.replace(/^gemini[- ]/i,"Gemini ").replace(/pro[- ](\d+)[- ](\d+)/i,"Pro $1.$2").replace(/pro[- ](\d+)/i,"Pro $1").replace(/-/g," ").replace(/\s+/g," ").trim();return o.length>30?o.slice(0,30)+"…":o}const l=r.replace(/-/g," ").replace(/^\w/,o=>o.toUpperCase()).replace(/\s+/g," ").trim();return l.length>30?l.slice(0,30)+"…":l}function $e(a,r){return a.length<=r?a:`${a.slice(0,r)}…`}function as(a){if(!a)return null;const r=Object.entries(a);return r.length===0?null:r.map(([c,l])=>{const o=typeof l=="string"?l:(()=>{try{return JSON.stringify(l)}catch{return String(l)}})();return`${c}=${$e(o,50)}`}).join(", ")}function is(a){if(a===void 0)return null;if(typeof a=="string")return $e(a,200);try{return $e(JSON.stringify(a),200)}catch{return $e(String(a),200)}}function lt(a){if(!a||a.length===0)return null;const r=(p,F)=>{const B=p.status==="running",D=p.status==="completed"&&p.isError,E=as(p.args),T=is(p.result),z=B?E:T?`result: ${T}`:E?`args: ${E}`:null,A=B?"running":D?"error":"completed";return t.jsxs("details",{className:`chat-tool-call${B?" chat-tool-call--running":""}${D?" chat-tool-call--error":""}`,open:B,children:[t.jsxs("summary",{children:[t.jsx("span",{className:"chat-tool-call-status-dot","aria-hidden":"true"}),t.jsx("span",{className:"chat-tool-call-name",children:p.toolName}),z&&t.jsx("span",{className:"chat-tool-call-preview",title:z,children:z}),t.jsx("span",{className:"chat-tool-call-status-text",children:A})]}),t.jsxs("div",{className:"chat-tool-call-content",children:[E&&t.jsxs("div",{className:"chat-tool-call-row",children:[t.jsx("span",{className:"chat-tool-call-label",children:"args"}),t.jsx("span",{className:"chat-tool-call-value",children:E})]}),T&&t.jsxs("div",{className:`chat-tool-call-row${D?" chat-tool-call-row--error":""}`,children:[t.jsx("span",{className:"chat-tool-call-label",children:"result"}),t.jsx("span",{className:"chat-tool-call-value",children:T})]})]})]},`${p.toolName}-${F}`)},c="chat-tool-calls";if(a.length===1)return t.jsxs("div",{className:c,"data-testid":"chat-tool-calls",children:[t.jsxs("div",{className:"chat-tool-calls-header",children:[t.jsx(st,{size:12,"aria-hidden":"true"}),t.jsx("span",{children:"Tool calls"})]}),r(a[0],0)]});const l=a.filter(p=>p.status==="running").length,o=a.filter(p=>p.status==="completed"&&p.isError).length,C=l>0,M=Array.from(new Set(a.map(p=>p.toolName))),R=M.slice(0,5),k=Math.max(0,M.length-R.length),V=k>0?`${R.join(", ")}, +${k} more`:R.join(", "),I=C?`(${l} running)`:o>0?`(${o} ${o===1?"error":"errors"})`:null;return t.jsx("div",{className:c,"data-testid":"chat-tool-calls",children:t.jsxs("details",{className:"chat-tool-calls-group","data-testid":"chat-tool-calls-group",open:C,children:[t.jsxs("summary",{className:"chat-tool-calls-group-summary",children:[t.jsx(st,{size:12,"aria-hidden":"true"}),t.jsxs("span",{children:[a.length," tool calls"]}),t.jsx("span",{className:"chat-tool-calls-names",title:V,children:V}),I&&t.jsx("span",{className:"chat-tool-calls-group-status",children:I})]}),a.map((p,F)=>r(p,F))]})})}const rs={pre:({children:a,...r})=>t.jsx("pre",{...r,className:"chat-markdown-pre",children:a}),table:({children:a,...r})=>t.jsx("table",{...r,className:"chat-markdown-table",children:a})},Pe="__fn_agent__",ls=["image/png","image/jpeg","image/gif","image/webp","text/plain","application/json","text/yaml","text/markdown","text/csv","application/xml","text/x-log"];function ct(a){const r=/(^|[\s])\/([^\s]*)$/.exec(a);if(!r)return null;const c=r[1]??"",l=r[2]??"",o=r.index+c.length;return{filter:l,start:o,end:a.length}}function cs(a,r){const c=a.slice(0,r),l=/(^|[\s\n])@([\w-]*)$/.exec(c);if(!l)return null;const o=l[2]??"",C=c.length-o.length-1;return{filter:o,start:C,end:r}}function os({projectId:a,onClose:r,onCreate:c}){const[l,o]=s.useState("agent"),[C,M]=s.useState([]),[R,k]=s.useState(!0),[V,I]=s.useState(""),[p,F]=s.useState([]),[B,D]=s.useState(!0),[E,T]=s.useState(""),[z,A]=s.useState([]),[G,H]=s.useState([]);s.useEffect(()=>{let u=!1;return k(!0),ze(void 0,a).then(w=>{u||M(w)}).catch(()=>{u||M([])}).finally(()=>{u||k(!1)}),()=>{u=!0}},[a]),s.useEffect(()=>{D(!0),es().then(u=>{F(u.models),A(u.favoriteProviders),H(u.favoriteModels)}).catch(()=>{F([]),A([]),H([])}).finally(()=>{D(!1)})},[]);const se=s.useCallback(async u=>{const w=z,P=w.includes(u)?w.filter(O=>O!==u):[u,...w];A(P);try{await nt({favoriteProviders:P,favoriteModels:G})}catch{A(w)}},[z,G]),ne=s.useCallback(async u=>{const w=G,P=w.includes(u)?w.filter(O=>O!==u):[u,...w];H(P);try{await nt({favoriteProviders:z,favoriteModels:P})}catch{H(w)}},[G,z]),$=u=>{if(u.preventDefault(),l==="agent"){if(!V)return;c({agentId:V});return}if(!E)return;const w=E.indexOf("/");if(w<=0)return;const x=E.slice(0,w),P=E.slice(w+1);c({agentId:Pe,modelProvider:x,modelId:P})},K=l==="agent"?!V:!E;return t.jsx("div",{className:"chat-new-dialog-backdrop",onClick:r,role:"dialog","aria-modal":"true",children:t.jsxs("div",{className:"chat-new-dialog",onClick:u=>u.stopPropagation(),children:[t.jsx("h3",{children:"New Chat"}),t.jsxs("div",{className:"chat-new-dialog-mode-toggle","data-testid":"chat-new-dialog-mode-toggle",children:[t.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="agent"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-agent",onClick:()=>{o("agent"),T("")},children:"Agent"}),t.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="model"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-model",onClick:()=>{o("model"),I("")},children:"Model"})]}),t.jsxs("form",{onSubmit:$,children:[l==="agent"&&t.jsxs("label",{className:"chat-new-dialog-model-label",children:["Agent",R?t.jsx("div",{className:"chat-new-dialog-loading",children:"Loading agents..."}):C.length===0?t.jsx("div",{className:"chat-new-dialog-empty",children:"No agents available"}):t.jsx("div",{className:"chat-new-dialog-agent-list",children:C.map(u=>t.jsxs("button",{type:"button",className:`chat-new-dialog-agent-item${V===u.id?" chat-new-dialog-agent-item--selected":""}`,onClick:()=>I(u.id),"data-testid":`agent-option-${u.id}`,children:[t.jsx(Ae,{size:16}),t.jsx("span",{className:"chat-new-dialog-agent-name",children:u.name}),t.jsx("span",{className:"chat-new-dialog-agent-role",children:u.role})]},u.id))})]}),l==="model"&&t.jsx("div",{className:"chat-new-dialog-model-dropdown","data-testid":"chat-new-dialog-model-section",children:B?t.jsx("div",{className:"chat-new-dialog-loading",children:"Loading models..."}):t.jsx(ts,{models:p,value:E,onChange:T,label:"Model",placeholder:"Select a model",favoriteProviders:z,onToggleFavorite:se,favoriteModels:G,onToggleModelFavorite:ne})}),t.jsxs("div",{className:"chat-new-dialog-actions",children:[t.jsx("button",{type:"button",className:"btn btn-sm",onClick:r,children:"Cancel"}),t.jsx("button",{type:"submit",className:"btn btn-sm btn-primary",disabled:K,children:"Create"})]})]})]})})}function gs({projectId:a,addToast:r}){const{activeSession:c,sessionsLoading:l,messages:o,messagesLoading:C,isStreaming:M,streamingText:R,streamingThinking:k,streamingToolCalls:V,selectSession:I,createSession:p,archiveSession:F,deleteSession:B,sendMessage:D,stopStreaming:E,pendingMessage:T,clearPendingMessage:z,searchQuery:A,setSearchQuery:G,filteredSessions:H}=ns(a),[se,ne]=s.useState(!1),[$,K]=s.useState(""),[u,w]=s.useState(null),[x,P]=s.useState(null),[O,Se]=s.useState(!0),[Q,je]=s.useState(new Map),[oe,ae]=s.useState([]),[ye,ie]=s.useState(!0),[X,q]=s.useState(!1),[re,ge]=s.useState(""),[fe,de]=s.useState(0),[pe,le]=s.useState(""),[W,Y]=s.useState(!1),[be,d]=s.useState(0),[g,h]=s.useState(-1),[j,Z]=s.useState(()=>new Set),[U,L]=s.useState([]),[we,b]=s.useState(!1),[,f]=s.useState(!1),[S,N]=s.useState({top:0,left:0}),m=It({projectId:a}),ce=s.useCallback(e=>{if(!e||!m.mentionActive)return;const n=e.getBoundingClientRect();N({top:n.top-260,left:n.left+8})},[m.mentionActive]),Oe=s.useRef(null),ee=s.useRef(null),Ue=s.useRef(null),_=s.useRef(null),He=s.useRef(null),Ve=s.useRef([]),ke=s.useRef(0),ue=zt()==="mobile",{keyboardOverlap:Ce,viewportHeight:Be}=Ot({enabled:ue&&!!c}),ot=Ce>0?{"--keyboard-overlap":`${Ce}px`,...Be!==null?{"--vv-height":`${Be}px`}:{}}:{},J=s.useMemo(()=>{const e=re.trim().toLowerCase();return(e?oe.filter(i=>i.name.toLowerCase().includes(e)):oe).slice(0,10)},[oe,re]),xe=s.useMemo(()=>Array.from(Q.values()),[Q]),he=s.useMemo(()=>{const e=pe.trim().toLowerCase();return e?xe.filter(n=>n.name.toLowerCase().includes(e)):xe},[xe,pe]),Ge=s.useMemo(()=>{const e=new Map;for(const n of xe)e.set(n.name.toLowerCase(),n);return e},[xe]);s.useEffect(()=>{de(0)},[J]),s.useEffect(()=>{d(0)},[pe,W]),s.useEffect(()=>()=>{ee.current!==null&&window.clearTimeout(ee.current)},[]),s.useEffect(()=>{Oe.current?.scrollIntoView({behavior:"smooth"})},[o,R]),s.useEffect(()=>{if(Ce<=0)return;const e=Ue.current;e&&(e.scrollTop=e.scrollHeight)},[Ce]),s.useEffect(()=>{const e=()=>w(null);if(u)return document.addEventListener("click",e),()=>document.removeEventListener("click",e)},[u]),s.useEffect(()=>{let e=!1;const n=a;return ze(void 0,a).then(i=>{if(e||n!==a)return;const v=new Map;for(const y of i)v.set(y.id,y);je(v)}).catch(()=>{}),()=>{e=!0}},[a]),s.useEffect(()=>{let e=!1;return ie(!0),Ut(a).then(n=>{e||ae(n)}).catch(()=>{e||ae([])}).finally(()=>{e||ie(!1)}),()=>{e=!0}},[a]),s.useEffect(()=>{Ve.current=U},[U]),s.useEffect(()=>()=>{for(const e of Ve.current)e.previewUrl&&URL.revokeObjectURL(e.previewUrl)},[]);const Me=s.useCallback(e=>{if(!e||e.length===0)return;const n=[];for(const i of Array.from(e)){if(!ls.includes(i.type))continue;const v=i.type.startsWith("image/");n.push({file:i,previewUrl:v?URL.createObjectURL(i):""})}n.length>0&&L(i=>[...i,...n])},[]),dt=s.useCallback(e=>{L(n=>{const i=n[e];return i?.previewUrl&&URL.revokeObjectURL(i.previewUrl),n.filter((v,y)=>y!==e)})},[]),ut=s.useCallback(e=>{const n=e.clipboardData?.files;if(!n||n.length===0)return;const i=Array.from(n).filter(v=>v.type.startsWith("image/"));i.length!==0&&Me(i)},[Me]),ht=s.useCallback(async e=>{try{await p(e),ne(!1),ue&&Se(!1)}catch{r("Failed to create chat session","error")}},[p,r,ue]),Re=s.useCallback(()=>{const e=$.trim(),n=U.map(i=>i.file);!e&&n.length===0||!c||(K(""),q(!1),ge(""),Y(!1),le(""),h(-1),D(e,n),L(i=>{for(const v of i)v.previewUrl&&URL.revokeObjectURL(v.previewUrl);return[]}))},[$,U,c,D]),De=s.useCallback(e=>{K(n=>{const i=ct(n);if(!i)return n;const v=`/skill:${e.name} `,y=n.slice(0,i.start)+v+n.slice(i.end);return window.requestAnimationFrame(()=>{_.current&&(_.current.style.height="auto",_.current.style.height=`${Math.min(_.current.scrollHeight,120)}px`,_.current.focus())}),y}),q(!1),ge(""),de(0)},[]),Ee=s.useCallback(e=>{const n=_.current;if(!n||g<0)return;const i=n.selectionStart??ke.current,v=n.selectionEnd??i,y=Math.max(i,v),Ne=Math.min(g,y),me=`${`@${e.name.replace(/\s+/g,"_")}`} `,_e=$.slice(0,Ne)+me+$.slice(y),ve=Ne+me.length;K(_e),Y(!1),le(""),d(0),h(-1),window.requestAnimationFrame(()=>{_.current&&(_.current.style.height="auto",_.current.style.height=`${Math.min(_.current.scrollHeight,120)}px`,_.current.focus(),_.current.setSelectionRange(ve,ve))})},[g,$]),mt=s.useCallback(e=>{const n=/@([\w-]+)/g,i=[];let v=0,y=n.exec(e);for(;y;){const[Ne,Ye=""]=y,me=y.index;me>v&&i.push(e.slice(v,me));const _e=Ye.replace(/_/g," ").toLowerCase(),ve=Ge.get(_e);ve?i.push(t.jsxs("span",{className:"chat-mention-chip",children:["@",ve.name.replace(/\s+/g,"_")]},`${ve.id}-${me}`)):i.push(Ne),v=me+Ne.length,y=n.exec(e)}return v<e.length&&i.push(e.slice(v)),i.length===0?e:i},[Ge]),Ke=s.useCallback(e=>c?`/api/chat/sessions/${encodeURIComponent(c.id)}/attachments/${encodeURIComponent(e)}`:"",[c]),gt=s.useCallback(e=>!e||e.length===0?null:t.jsx("div",{className:"chat-message-attachments",children:e.map(n=>{const i=n.mimeType.startsWith("image/"),v=n.id||n.filename,y=Ke(n.filename);return i?t.jsx("a",{className:"chat-message-attachment-link","data-testid":"chat-message-attachment",href:y,target:"_blank",rel:"noopener noreferrer",children:t.jsx("img",{className:"chat-message-attachment",src:y,alt:n.originalName})},v):t.jsxs("a",{className:"chat-message-attachment-file","data-testid":"chat-message-attachment",href:y,target:"_blank",rel:"noopener noreferrer",children:[t.jsx(Ht,{size:14}),t.jsx("span",{children:n.originalName})]},v)})}),[Ke]),ft=s.useCallback(e=>{if(ke.current=e.currentTarget.selectionStart??ke.current,m.mentionActive&&m.files.length>0){if(m.handleKeyDown(e,$),e.key==="Enter"||e.key==="Tab"){const n=m.files[m.selectedIndex];if(n){const i=m.selectFile(n,$);K(i),m.dismissMention(),f(!1)}}return}if(W&&e.key==="ArrowDown"){e.preventDefault(),he.length>0&&d(n=>(n+1)%he.length);return}if(W&&e.key==="ArrowUp"){e.preventDefault(),he.length>0&&d(n=>n===0?he.length-1:n-1);return}if(W&&e.key==="Enter"){e.preventDefault();const n=he[be]??he[0];n&&Ee(n);return}if(W&&e.key==="Escape"){e.preventDefault(),Y(!1),le(""),h(-1);return}if(X&&e.key==="ArrowDown"){e.preventDefault(),J.length>0&&de(n=>(n+1)%J.length);return}if(X&&e.key==="ArrowUp"){e.preventDefault(),J.length>0&&de(n=>n===0?J.length-1:n-1);return}if(X&&(e.key==="Enter"||e.key==="Tab")&&J.length>0){e.preventDefault();const n=J[fe]??J[0];n&&De(n);return}if(X&&e.key==="Escape"){e.preventDefault(),q(!1);return}e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Re())},[W,he,be,Ee,X,J,fe,De,Re,m,$]),Te=s.useCallback((e,n)=>{const i=cs(e,n);if(i){Y(!0),le(i.filter),h(i.start);return}Y(!1),le(""),h(-1)},[]),pt=s.useCallback(e=>{const n=e.target,i=n.value,v=n.selectionStart??i.length;ke.current=v,K(i);const y=ct(i);y?(q(!0),ge(y.filter)):(q(!1),ge("")),Te(i,v),m.detectMention(i,v),f(m.mentionActive),m.mentionActive&&ce(n),n.style.height="auto",n.style.height=`${Math.min(n.scrollHeight,120)}px`},[Te]),Fe=s.useCallback(e=>{const n=e.currentTarget,i=n.selectionStart??n.value.length;ke.current=i,Te(n.value,i),m.detectMention(n.value,i),f(m.mentionActive),m.mentionActive&&ce(n)},[Te,m,ce]),xt=s.useCallback(e=>{e.key!=="Escape"&&Fe(e)},[Fe]),vt=s.useCallback(()=>{ee.current!==null&&window.clearTimeout(ee.current),ee.current=window.setTimeout(()=>{q(!1),Y(!1),le(""),h(-1),f(!1),m.dismissMention(),ee.current=null},120)},[m]),St=s.useCallback(()=>{ee.current!==null&&(window.clearTimeout(ee.current),ee.current=null)},[]),bt=s.useCallback(async e=>{w(null);try{await F(e),r("Conversation archived","success")}catch{r("Failed to archive conversation","error")}},[F,r]),wt=s.useCallback(async e=>{P(null),w(null);try{await B(e),r("Conversation deleted","success")}catch{r("Failed to delete conversation","error")}},[B,r]),kt=s.useCallback(e=>{I(e),ue&&Se(!1)},[I,ue]),Nt=s.useCallback(()=>{I(""),Se(!0)},[I]),jt=()=>t.jsxs("div",{className:"chat-empty-state",children:[t.jsx(Zt,{size:48,strokeWidth:1.5}),t.jsx("h2",{children:"Start a new conversation"}),t.jsxs("button",{className:"btn btn-primary",onClick:()=>ne(!0),children:[t.jsx(Ze,{size:16}),"New Chat"]})]}),te=rt(c?.modelProvider,c?.modelId),qe=c?.agentId===Pe?te??"Fusion":c?.title||Q.get(c?.agentId??"")?.name||c?.agentId||"Chat",yt=!!(te&&te!==qe),Le=Q.get(c?.agentId??"")?.name||(c?.agentId===Pe?te??"Fusion":c?.agentId?.slice(0,30)??"Fusion"),Je=!!(te&&te!==Le),Ct=T.length>50?`${T.slice(0,50)}…`:T,Qe=s.useCallback(e=>{Z(n=>{const i=new Set(n);return i.has(e)?i.delete(e):i.add(e),i})},[]),We=s.useCallback((e,n=!1)=>n?t.jsx("div",{className:"chat-message-content chat-message-content--plain",children:e}):t.jsx("div",{className:"chat-message-content chat-message-content--markdown",children:t.jsx(Vt,{remarkPlugins:[Bt],components:rs,children:e})}),[]);return t.jsxs("div",{className:"chat-view",children:[t.jsxs("div",{className:`chat-sidebar${O?"":" chat-sidebar--hidden"}`,children:[t.jsx("div",{className:"chat-sidebar-search",children:t.jsxs("div",{className:"chat-sidebar-search-wrapper",children:[t.jsx(Gt,{size:14,className:"chat-sidebar-search-icon"}),t.jsx("input",{type:"text",className:"chat-sidebar-search",placeholder:"Search conversations...",value:A,onChange:e=>G(e.target.value),"data-testid":"chat-search-input"})]})}),t.jsx("div",{className:"chat-session-list chat-sidebar-list",children:l?t.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"Loading..."}):H.length===0?t.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"No conversations yet"}):H.map(e=>t.jsxs("div",{className:`chat-session-item${c?.id===e.id?" chat-session-item--active":""}`,onClick:()=>kt(e.id),onContextMenu:n=>{n.preventDefault(),w({sessionId:e.id,x:n.clientX,y:n.clientY})},"data-testid":`chat-session-${e.id}`,children:[t.jsx("button",{className:"chat-session-delete-btn",onClick:n=>{n.stopPropagation(),P(e.id)},"data-testid":"chat-session-delete-btn","aria-label":"Delete conversation",children:t.jsx(Xe,{size:14})}),t.jsx("div",{className:"chat-session-title",children:e.title||"Untitled"}),t.jsx("div",{className:"chat-session-preview",children:e.lastMessagePreview||"No messages"}),t.jsxs("div",{className:"chat-session-meta",children:[t.jsx("span",{children:Q.get(e.agentId)?.name||(e.agentId===Pe?rt(e.modelProvider,e.modelId)??"Fusion":e.agentId.slice(0,30))}),t.jsx("span",{children:e.updatedAt?it(e.updatedAt):""})]})]},e.id))}),t.jsx("div",{className:"chat-sidebar-footer",children:t.jsxs("button",{className:"btn btn-sm btn-primary chat-sidebar-footer-btn",onClick:()=>ne(!0),"data-testid":"chat-new-btn",children:[t.jsx(Ze,{size:14}),"New Chat"]})})]}),u&&t.jsxs("div",{className:"chat-session-context-menu",style:{top:u.y,left:u.x},onClick:e=>e.stopPropagation(),children:[t.jsxs("button",{onClick:()=>bt(u.sessionId),"data-testid":"chat-context-archive",children:[t.jsx(Kt,{size:14}),"Archive"]}),t.jsxs("button",{onClick:()=>{w(null),P(u.sessionId)},"data-testid":"chat-context-delete",children:[t.jsx(Xe,{size:14}),"Delete"]})]}),x&&t.jsx("div",{className:"chat-new-dialog-backdrop",onClick:()=>P(null),children:t.jsxs("div",{className:"chat-new-dialog",onClick:e=>e.stopPropagation(),children:[t.jsx("h3",{children:"Delete Conversation?"}),t.jsx("p",{style:{fontSize:"14px",color:"var(--text-secondary)",marginBottom:"16px"},children:"This action cannot be undone. All messages in this conversation will be permanently deleted."}),t.jsxs("div",{className:"chat-new-dialog-actions",children:[t.jsx("button",{className:"btn btn-sm",onClick:()=>P(null),children:"Cancel"}),t.jsx("button",{className:"btn btn-sm btn-danger",onClick:()=>void wt(x),children:"Delete"})]})]})}),t.jsxs("div",{className:"chat-thread",style:ot,children:[(c||!ue)&&t.jsxs("div",{className:"chat-thread-header",children:[ue&&c&&t.jsx("button",{className:"btn-icon",onClick:Nt,"data-testid":"chat-back-btn",children:t.jsx(qt,{size:16})}),t.jsx(Ae,{size:16}),t.jsx("span",{className:"chat-thread-header-title",children:qe}),yt&&t.jsx("span",{className:"chat-model-tag",children:te})]}),t.jsxs("div",{className:"chat-messages",ref:Ue,children:[C?t.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"Loading messages..."}):o.length===0&&!c?jt():o.length===0&&c?t.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"No messages yet. Start the conversation!"}):t.jsxs(t.Fragment,{children:[o.map(e=>{const n=e.role==="assistant",i=j.has(e.id);return t.jsxs("div",{className:`chat-message chat-message--${e.role}`,"data-testid":`chat-message-${e.id}`,children:[n&&t.jsxs("div",{className:"chat-message-avatar",children:[t.jsx(Ae,{size:14}),t.jsx("span",{children:Le}),Je&&t.jsx("span",{className:"chat-model-tag",children:te}),t.jsx("button",{type:"button",className:`chat-message-render-toggle${i?" chat-message-render-toggle--plain":""}`,"data-testid":"chat-message-render-toggle","aria-label":i?"Show rendered markdown":"Show plain text",onClick:()=>Qe(e.id),children:i?t.jsx(et,{size:14}):t.jsx(tt,{size:14})})]}),n?We(e.content,i):t.jsx("div",{className:"chat-message-content",children:mt(e.content)}),lt(e.toolCalls),e.thinkingOutput&&t.jsxs("details",{className:"chat-message-thinking",children:[t.jsx("summary",{children:"Thinking"}),t.jsx("pre",{className:"chat-message-thinking-content",children:e.thinkingOutput})]}),gt(e.attachments),t.jsx("div",{className:"chat-message-time",children:it(e.createdAt)})]},e.id)}),M&&t.jsxs("div",{className:"chat-message chat-message--assistant chat-message--streaming",children:[t.jsxs("div",{className:"chat-message-avatar",children:[t.jsx(Ae,{size:14}),t.jsx("span",{children:Le}),Je&&t.jsx("span",{className:"chat-model-tag",children:te}),t.jsx("button",{type:"button",className:`chat-message-render-toggle${j.has("__streaming__")?" chat-message-render-toggle--plain":""}`,"data-testid":"chat-message-render-toggle","aria-label":j.has("__streaming__")?"Show rendered markdown":"Show plain text",onClick:()=>Qe("__streaming__"),children:j.has("__streaming__")?t.jsx(et,{size:14}):t.jsx(tt,{size:14})})]}),R?We(R,j.has("__streaming__")):t.jsx("div",{className:"chat-message-content chat-message-content--waiting",children:k?"Thinking…":"Connecting…"}),lt(V),k&&t.jsxs("details",{className:"chat-message-thinking",children:[t.jsx("summary",{children:"Thinking"}),t.jsx("pre",{className:"chat-message-thinking-content",children:k})]}),t.jsxs("div",{className:"chat-typing-indicator",children:[t.jsx("span",{}),t.jsx("span",{}),t.jsx("span",{})]})]})]}),t.jsx("div",{ref:Oe})]}),c&&t.jsxs("div",{className:"chat-input-area",children:[t.jsx("input",{ref:He,type:"file",accept:"image/*,.txt,.json,.yaml,.yml,.log,.csv,.xml,.md",multiple:!0,style:{display:"none"},onChange:e=>{Me(e.target.files),e.target.value=""}}),X&&t.jsx("div",{className:"chat-skill-menu","data-testid":"chat-skill-menu",role:"listbox","aria-label":"Skill suggestions",children:ye?t.jsx("div",{className:"chat-skill-menu-empty",children:"Loading skills…"}):J.length===0?t.jsx("div",{className:"chat-skill-menu-empty",children:re?"No skills found":"No skills available"}):J.map((e,n)=>t.jsxs("button",{type:"button",role:"option","aria-selected":n===fe,className:`chat-skill-menu-item${n===fe?" chat-skill-menu-item--highlighted":""}`,onMouseDown:i=>i.preventDefault(),onMouseEnter:()=>de(n),onClick:()=>De(e),children:[t.jsx("span",{className:"chat-skill-menu-item-name",children:e.name}),t.jsx("span",{className:"chat-skill-menu-item-description",title:e.relativePath,children:e.relativePath})]},e.id))}),U.length>0&&t.jsx("div",{className:"chat-attachment-previews","data-testid":"chat-attachment-previews",children:U.map((e,n)=>t.jsxs("div",{className:"chat-attachment-preview","data-testid":`chat-attachment-preview-${n}`,children:[e.previewUrl?t.jsx("img",{src:e.previewUrl,alt:e.file.name}):t.jsx("span",{className:"chat-attachment-preview-name",children:e.file.name}),t.jsx("button",{type:"button",className:"chat-attachment-remove",onClick:()=>dt(n),"data-testid":`chat-attachment-remove-${n}`,"aria-label":`Remove ${e.file.name}`,children:"×"})]},e.previewUrl||`${e.file.name}-${n}`))}),t.jsxs("div",{className:"chat-input-row",children:[t.jsx("button",{type:"button",className:"btn-icon chat-attach-btn","data-testid":"chat-attach-btn","aria-label":"Attach files",onClick:()=>He.current?.click(),children:t.jsx(Jt,{size:16})}),t.jsxs("div",{className:`chat-input-wrapper${we?" chat-input-wrapper--dragover":""}`,onDragOver:e=>{e.preventDefault(),b(!0)},onDragLeave:()=>b(!1),onDrop:e=>{e.preventDefault(),b(!1),Me(e.dataTransfer.files)},children:[t.jsx("textarea",{ref:_,className:"chat-input-textarea",placeholder:"Type a message...",value:$,onChange:pt,onKeyDown:ft,onKeyUp:xt,onClick:Fe,onBlur:vt,onFocus:St,onPaste:ut,rows:1,"data-testid":"chat-input"}),t.jsx(Qt,{agents:xe,filter:pe,highlightedIndex:be,visible:W,onSelect:Ee,position:"below"}),t.jsx(Wt,{visible:m.mentionActive&&!W,position:S,files:m.files,selectedIndex:m.selectedIndex,onSelect:e=>{const n=m.selectFile(e,$);K(n),m.dismissMention(),f(!1),_.current?.focus()},loading:m.loading}),T&&t.jsxs("div",{className:"chat-pending-message","data-testid":"chat-pending-indicator",children:[t.jsx("span",{children:`Queued: ${Ct}`}),t.jsx("button",{type:"button",className:"chat-pending-message-dismiss","aria-label":"Dismiss queued message","data-testid":"chat-pending-dismiss",onClick:z,children:"×"})]})]}),M?t.jsx("button",{className:"chat-input-stop",onClick:E,"aria-label":"Stop generation","data-testid":"chat-stop-btn",children:t.jsx(Yt,{size:14})}):t.jsx("button",{className:"chat-input-send",onClick:()=>void Re(),disabled:!$.trim()&&U.length===0,"data-testid":"chat-send-btn",children:t.jsx(Xt,{size:16})})]})]})]}),se&&t.jsx(os,{projectId:a,onClose:()=>ne(!1),onCreate:ht})]})}export{gs as ChatView};
@@ -1 +0,0 @@
1
- import{r as a,j as e}from"./vendor-react-K0fH_qHe.js";import{am as x,an as j}from"./index-Bs3RZu5I.js";import"./vendor-xterm-DzcZoU0P.js";const w={pending:"Pending",running:"Running",completed:"Completed",failed:"Failed",cancelled:"Cancelled"};function b({projectId:n,addToast:h}){const[r,m]=a.useState([]),[d,v]=a.useState(null),[t,o]=a.useState(!0),[c,u]=a.useState(null),i=a.useCallback(async()=>{o(!0),u(null);try{const[s,l]=await Promise.all([x({limit:50},n),j(n)]);m(s.runs),v(l)}catch(s){const l=s instanceof Error?s.message:"Failed to load research runs";u(l),h?.(l,"error")}finally{o(!1)}},[n,h]);a.useEffect(()=>{i()},[i]);const _=a.useMemo(()=>r.some(s=>s.status==="completed"&&s.results?.summary),[r]);return e.jsxs("section",{className:"research-view","aria-label":"Research view",children:[e.jsxs("header",{className:"research-view__header",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"research-view__title",children:"Research"}),e.jsx("p",{className:"research-view__subtitle",children:"Track synthesis runs, source collection, and export artifacts."})]}),e.jsx("button",{className:"btn",type:"button",onClick:()=>void i(),children:"Refresh"})]}),t&&e.jsx("div",{className:"research-view__state card","data-testid":"research-state-loading",children:"Loading research runs…"}),!t&&c&&e.jsxs("div",{className:"research-view__state research-view__state--error card","data-testid":"research-state-error",children:[e.jsx("p",{children:c}),e.jsx("button",{className:"btn btn-danger",type:"button",onClick:()=>void i(),children:"Retry"})]}),!t&&!c&&r.length===0&&e.jsx("div",{className:"research-view__state card","data-testid":"research-state-empty",children:"No research runs yet. Start a run from the API or upcoming orchestration workflow."}),!t&&!c&&r.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"research-view__stats","data-testid":"research-state-running",children:[e.jsxs("div",{className:"card research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Total Runs"}),e.jsx("div",{className:"research-view__stat-value",children:d?.total??r.length})]}),e.jsxs("div",{className:"card research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Running"}),e.jsx("div",{className:"research-view__stat-value",children:d?.byStatus.running??0})]}),e.jsxs("div",{className:"card research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Completed"}),e.jsx("div",{className:"research-view__stat-value",children:d?.byStatus.completed??0})]})]}),e.jsx("div",{className:"research-view__list",children:r.map(s=>e.jsxs("article",{className:"card research-view__run-card",children:[e.jsxs("div",{className:"research-view__run-head",children:[e.jsx("span",{className:`card-status-badge ${s.status==="failed"?"research-view__status-badge--failed":`card-status-badge--${s.status==="pending"?"todo":s.status==="running"?"in-progress":s.status==="completed"?"done":"archived"}`}`,children:w[s.status]}),e.jsx("span",{className:"card-id",children:s.id})]}),e.jsx("h3",{className:"research-view__run-title",children:s.topic||s.query}),e.jsx("p",{className:"research-view__run-query",children:s.query}),s.results?.summary&&e.jsx("p",{"data-testid":"research-state-results",children:s.results.summary})]},s.id))}),!_&&e.jsx("p",{className:"research-view__hint",children:"Runs are active, but no summarized results are available yet."})]})]})}export{b as ResearchView};
@@ -1 +0,0 @@
1
- .research-view{display:flex;flex-direction:column;gap:var(--space-lg);padding:var(--space-lg)}.research-view__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-md)}.research-view__title{margin:0;color:var(--text)}.research-view__subtitle{margin:var(--space-xs) 0 0;color:var(--text-muted)}.research-view__state{display:flex;flex-direction:column;gap:var(--space-md);padding:var(--space-lg)}.research-view__state--error{border-color:var(--color-error);background:color-mix(in srgb,var(--color-error) 12%,var(--card))}.research-view__stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-md)}.research-view__stat-card{padding:var(--space-lg)}.research-view__stat-label{color:var(--text-muted)}.research-view__stat-value{margin-top:var(--space-xs);color:var(--text);font-family:var(--font-mono)}.research-view__list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-md)}.research-view__run-card{display:flex;flex-direction:column;gap:var(--space-sm)}.research-view__run-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}.research-view__status-badge--failed{border-color:var(--color-error);background:color-mix(in srgb,var(--color-error) 18%,transparent);color:var(--color-error)}.research-view__run-title{margin:0;color:var(--text)}.research-view__run-query,.research-view__hint{margin:0;color:var(--text-muted)}@media(max-width:768px){.research-view{padding:var(--space-md)}.research-view__header{flex-direction:column}.research-view__stats,.research-view__list{grid-template-columns:minmax(0,1fr)}}