sparkecoder 0.1.123 → 0.1.124

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 (123) hide show
  1. package/dist/cli.js +77 -42
  2. package/dist/cli.js.map +1 -1
  3. package/dist/skills/default/recording.md +2 -2
  4. package/package.json +1 -1
  5. package/src/skills/default/recording.md +2 -2
  6. package/web/.next/BUILD_ID +1 -1
  7. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  8. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  9. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  10. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
  11. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  12. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  13. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  14. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
  15. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  16. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  17. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  18. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  19. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  20. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  21. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  22. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  23. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  31. package/web/.next/standalone/web/.next/server/app/agents.rsc +3 -3
  32. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +2 -2
  33. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +2 -2
  35. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +3 -3
  36. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  75. package/web/.next/standalone/web/.next/server/app/index.rsc +3 -3
  76. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  77. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
  79. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  83. package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
  84. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
  85. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +2 -2
  87. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  88. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_c87abaf4._.js → 2374f_12d55e68._.js} +1 -1
  92. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1f3f2d00._.js → 2374f_1c0639c2._.js} +1 -1
  93. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_a0d5caeb._.js → 2374f_28cd6777._.js} +1 -1
  94. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_570c34dc._.js → 2374f_5f47a9b7._.js} +1 -1
  95. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d8122230._.js → 2374f_aa218457._.js} +1 -1
  96. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_9c560f3a._.js → 2374f_f678a96f._.js} +1 -1
  97. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_38945fd9._.js → 2374f_fac4000d._.js} +1 -1
  98. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__4de426bd._.js → [root-of-the-server]__e5911ea8._.js} +4 -4
  99. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_62ca4286._.js → web_2966b3a3._.js} +2 -2
  100. package/web/.next/standalone/web/.next/server/chunks/ssr/web_4fe3c244._.js +1 -1
  101. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  102. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  103. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  104. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  105. package/web/.next/standalone/web/.next/static/chunks/{91988e253d5fa420.js → 4d95c15f712c9e06.js} +5 -5
  106. package/web/.next/standalone/web/.next/static/chunks/780c93257fac7d43.js +1 -0
  107. package/web/.next/standalone/web/.next/static/static/chunks/{91988e253d5fa420.js → 4d95c15f712c9e06.js} +5 -5
  108. package/web/.next/standalone/web/.next/static/static/chunks/780c93257fac7d43.js +1 -0
  109. package/web/.next/standalone/web/src/components/chat-interface.tsx +112 -1
  110. package/web/.next/static/chunks/{91988e253d5fa420.js → 4d95c15f712c9e06.js} +5 -5
  111. package/web/.next/static/chunks/780c93257fac7d43.js +1 -0
  112. package/web/.next/standalone/web/.next/static/chunks/f0f19357f3fb7cf8.js +0 -1
  113. package/web/.next/standalone/web/.next/static/static/chunks/f0f19357f3fb7cf8.js +0 -1
  114. package/web/.next/static/chunks/f0f19357f3fb7cf8.js +0 -1
  115. /package/web/.next/standalone/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_buildManifest.js +0 -0
  116. /package/web/.next/standalone/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_clientMiddlewareManifest.json +0 -0
  117. /package/web/.next/standalone/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_ssgManifest.js +0 -0
  118. /package/web/.next/standalone/web/.next/static/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_buildManifest.js +0 -0
  119. /package/web/.next/standalone/web/.next/static/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_clientMiddlewareManifest.json +0 -0
  120. /package/web/.next/standalone/web/.next/static/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_ssgManifest.js +0 -0
  121. /package/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_buildManifest.js +0 -0
  122. /package/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_clientMiddlewareManifest.json +0 -0
  123. /package/web/.next/static/{MP4p8_EldjbZ69dONoEcM → cYXZ7UzGc5TttFIXRRcSC}/_ssgManifest.js +0 -0
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,524741,e=>{"use strict";var t=e.i(739963);function r(){return(0,t.getApiUrl)()}async function n(e){let t=e?.role?`?role=${e.role}`:"",n=await fetch(`${r()}/sessions${t}`);return n.ok&&(await n.json().catch(()=>({}))).sessions||[]}async function o(e,t,n){await fetch(`${r()}/tasks/${e}/questions/${t}/answer`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({answer:n,answeredBy:"user"})})}async function s(e){if(!e||"undefined"===e||"null"===e)return null;let t=await fetch(`${r()}/sessions/${e}`);if(!t.ok)return null;let n=await t.json().catch(()=>null);return n&&"string"==typeof n.id?n:null}async function a(e){return(await fetch(`${r()}/sessions`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json()}async function i(e){await fetch(`${r()}/sessions/${e}`,{method:"DELETE"})}async function c(e){return(await fetch(`${r()}/tasks`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json()}async function l(e){return(await fetch(`${r()}/sessions/${e}/todos`)).json()}async function u(e){return(await fetch(`${r()}/sessions/${e}/plans`)).json()}async function f(e){return(await fetch(`${r()}/sessions/${e}/pending-input`)).json()}async function d(e,t){return(await fetch(`${r()}/sessions/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function g(e,t,r,n){return d(e,{toolApprovals:{[t]:r}})}async function h(e){let t=await fetch(`${r()}/sessions/${e}/messages`);return(await t.json()).messages||[]}async function p(e){let t=await fetch(`${r()}/agents/${e}/approvals`);return(await t.json()).pendingApprovals||[]}async function y(e,t){await fetch(`${r()}/agents/${e}/approve/${t}`,{method:"POST"})}async function w(e,t,n){await fetch(`${r()}/agents/${e}/reject/${t}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reason:n})})}async function S(e){return(await fetch(`${r()}/agents/${e}/stream`)).json()}async function m(e){return(await fetch(`${r()}/agents/${e}/abort`,{method:"POST"})).json()}function E(e,t,r){return new Promise(async(n,o)=>{let s=e.body?.getReader();if(!s){console.error("[SSE] No response body — cannot read stream"),o(Error("No response body"));return}let a=new TextDecoder,i="",c=0,l=0,u=0,f=0,d=0,g=Date.now(),h=!0===window.__sparkeSseDebug;console.log("[SSE] Stream reader started, status:",e.status);try{for(;;){let{done:e,value:n}=await s.read();if(e){console.log(`[SSE] Stream ended. Total events: ${c}, browser frames: ${l}, browser status: ${u}, parse errors: ${f}, bytes: ${d}`);break}d+=n?.byteLength??0;let o=(i+=a.decode(n,{stream:!0})).split("\n");for(let e of(i=o.pop()||"",o))if(e.startsWith("data: ")){let n=e.slice(6);if("[DONE]"===n){console.log("[SSE] Received [DONE] marker");continue}try{let e=JSON.parse(n);c++,"browser-frame"===e.type?(l++,1===l?console.log(`[SSE] First browser-frame received! dataSize=${e.data?.length??0}`):l%50==0&&console.log(`[SSE] Browser frame #${l}`)):"browser-status"===e.type&&(u++,console.log(`[SSE] Browser status event: connected=${e.connected} screencasting=${e.screencasting}`));let o=Date.now();o-g>1e4&&(console.log(`[SSE] Stats: events=${c} browserFrames=${l} bytes=${d} parseErrors=${f}`),g=o),"data-stream-id"===e.type&&r&&(console.log(`[SSE] Stream ID received: ${e.streamId}`),r(e.streamId)),h&&window.dispatchEvent(new CustomEvent("sparke:sse-event",{detail:{type:e.type,size:n.length,label:"string"==typeof e.label?e.label:void 0,toolName:"string"==typeof e.toolName?e.toolName:void 0}})),t(e)}catch(e){f++,console.warn("[SSE] Failed to parse event JSON",{error:e instanceof Error?e.message:String(e),dataLength:n.length,preview:n.slice(0,200),parseErrors:f}),h&&window.dispatchEvent(new CustomEvent("sparke:sse-parse-error",{detail:{error:e instanceof Error?e.message:String(e),size:n.length,preview:n.slice(0,200)}}))}}}n()}catch(e){console.error(`[SSE] Stream read error after ${c} events, ${d} bytes:`,e),o(e)}})}function $(e,t,n,o){let s=new AbortController;console.log(`[STREAM] runAgent: starting stream for session ${e}`);let a={prompt:t};return o?.attachments&&o.attachments.length>0&&(a.attachments=o.attachments),fetch(`${r()}/agents/${e}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a),signal:s.signal}).then(async t=>{console.log(`[STREAM] runAgent: response status=${t.status}, content-type=${t.headers.get("content-type")}`);let r=t.headers.get("x-stream-id");r&&(console.log(`[STREAM] runAgent: received stream ID ${r}`),o?.onStreamId&&o.onStreamId(r)),await E(t,n,o?.onStreamId),console.log(`[STREAM] runAgent: stream completed for session ${e}`)}).catch(t=>{"AbortError"!==t.name?console.error(`[STREAM] runAgent error for session ${e}:`,t):console.log(`[STREAM] runAgent: aborted for session ${e}`)}),()=>{console.log(`[STREAM] runAgent: abort called for session ${e}`),s.abort()}}function v(e,t,n){let o=new AbortController,s=new URLSearchParams;n?.streamId&&s.set("streamId",n.streamId),n?.resumeAt!==void 0&&s.set("resumeAt",String(n.resumeAt));let a=`${r()}/agents/${e}/watch${s.toString()?"?"+s.toString():""}`;return console.log(`[STREAM] watchStream: connecting to ${a}`),fetch(a,{signal:o.signal}).then(async r=>{if(console.log(`[STREAM] watchStream: response status=${r.status}, content-type=${r.headers.get("content-type")}`),!r.ok){let e=await r.json().catch(()=>({error:"Unknown error"}));console.error(`[STREAM] watchStream error: status=${r.status}`,e),t({type:"error",errorText:e.error||"Failed to watch stream"});return}let o=r.headers.get("x-stream-id");o&&(console.log(`[STREAM] watchStream: received stream ID ${o}`),n?.onStreamId&&n.onStreamId(o)),await E(r,t,n?.onStreamId),console.log(`[STREAM] watchStream: completed for session ${e}`)}).catch(t=>{"AbortError"!==t.name?console.error(`[STREAM] watchStream error for session ${e}:`,t):console.log(`[STREAM] watchStream: aborted for session ${e}`)}),()=>{console.log(`[STREAM] watchStream: abort called for session ${e}`),o.abort()}}function T(e,t,n){let o=new AbortController;return console.log(`[TERMINAL] Connecting to terminal stream: ${e}`),(async()=>{try{let s=await fetch(`${r()}/terminals/stream/${e}`,{signal:o.signal});if(!s.ok||!s.body)return void console.error(`[TERMINAL] Failed to connect to terminal stream ${e}: status=${s.status}`);console.log(`[TERMINAL] Connected to terminal stream ${e}`);let a=s.body.getReader(),i=new TextDecoder,c="",l=0;for(;;){let{done:r,value:o}=await a.read();if(r){console.log(`[TERMINAL] Stream ended for ${e} after ${l} output chunks`);break}let s=(c+=i.decode(o,{stream:!0})).split("\n\n");for(let r of(c=s.pop()||"",s)){if(!r.trim())continue;let o=r.match(/event:\s*(\w+)/),s=r.match(/data:\s*(.+)/);if(o&&s){let r=o[1];try{let o=JSON.parse(s[1]);"stdout"===r&&o.data?(l++,t(o.data)):"exit"===r&&(console.log(`[TERMINAL] Exit event for ${e}`),n?.())}catch{}}}}}catch(t){t instanceof Error&&"AbortError"!==t.name&&console.error(`[TERMINAL] Stream error for ${e}:`,t)}})(),()=>{console.log(`[TERMINAL] Aborting terminal stream ${e}`),o.abort()}}async function b(){return(await fetch(`${r()}/health/version`)).json()}async function R(){return(await fetch(`${r()}/health/api-keys`)).json()}async function A(e,t){return(await fetch(`${r()}/health/api-keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:e,apiKey:t})})).json()}async function O(e){let t=await fetch(`${r()}/sessions/${e}/checkpoints`);if(!t.ok)throw Error(`Failed to get checkpoints: ${t.statusText}`);return t.json()}async function k(e,t){let n=await fetch(`${r()}/sessions/${e}/revert/${t}`,{method:"POST"});if(!n.ok)throw Error((await n.json()).error||`Failed to revert: ${n.statusText}`);return n.json()}async function j(e){let t=await fetch(`${r()}/sessions/${e}/diff`);if(!t.ok)throw Error(`Failed to get diff: ${t.statusText}`);return t.json()}async function _(e){let t=await fetch(`${r()}/sessions/${e}/attachments`);if(!t.ok)throw Error(`Failed to get attachments: ${t.statusText}`);return t.json()}async function N(e,t,n){let o=new URLSearchParams;t&&o.set("query",t),n&&o.set("limit",String(n));let s=`${r()}/sessions/${e}/files${o.toString()?"?"+o.toString():""}`,a=await fetch(s);if(!a.ok)throw Error(`Failed to get workspace files: ${a.statusText}`);return a.json()}async function C(e,t){await fetch(`${r()}/agents/${e}/browser-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function I(e){let t=await fetch(`${r()}/sessions/${e}/session-files`);if(!t.ok){if(404===t.status)return[];throw Error(`Failed to get session files: ${t.statusText}`)}return(await t.json()).files||[]}async function L(e){let t=await fetch(`${r()}/sessions/${e}/browser-recording`);return t.ok&&(await t.json()).recordings||[]}async function M(e){try{let t=await fetch(`${r()}/agents/${e}/browser-stream`);if(!t.ok)return null;return t.json()}catch{return null}}e.s(["abortStream",()=>m,"answerAgentQuestion",()=>o,"approveExecution",()=>y,"checkVersion",()=>b,"createSession",()=>a,"createTask",()=>c,"deleteSession",()=>i,"getActiveStream",()=>S,"getApiKeys",()=>R,"getBrowserRecordings",()=>L,"getBrowserStreamStatus",()=>M,"getPendingApprovals",()=>p,"getPendingInput",()=>f,"getSession",()=>s,"getSessionAttachments",()=>_,"getSessionCheckpoints",()=>O,"getSessionDiff",()=>j,"getSessionFiles",()=>I,"getSessionMessages",()=>h,"getSessionPlans",()=>u,"getSessionTodos",()=>l,"getSessions",()=>n,"getWorkspaceFiles",()=>N,"rejectExecution",()=>w,"revertToCheckpoint",()=>k,"runAgent",()=>$,"sendBrowserInput",()=>C,"setApiKey",()=>A,"streamTerminal",()=>T,"updateSession",()=>d,"updateToolApproval",()=>g,"watchStream",()=>v])},310711,(e,t,r)=>{"use strict";var n=e.r(430878),o="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},s=n.useState,a=n.useEffect,i=n.useLayoutEffect,c=n.useDebugValue;function l(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!o(e,r)}catch(e){return!0}}var u="u"<typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var r=t(),n=s({inst:{value:r,getSnapshot:t}}),o=n[0].inst,u=n[1];return i(function(){o.value=r,o.getSnapshot=t,l(o)&&u({inst:o})},[e,r,t]),a(function(){return l(o)&&u({inst:o}),e(function(){l(o)&&u({inst:o})})},[e]),c(r),r};r.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:u},753257,(e,t,r)=>{"use strict";t.exports=e.r(310711)},262189,103393,e=>{"use strict";let t;var r=e.i(430878),n=e.i(753257);e.s(["ERROR_REVALIDATE_EVENT",()=>3,"FOCUS_EVENT",()=>0,"MUTATE_EVENT",()=>2,"RECONNECT_EVENT",()=>1],786254);var o=Object.prototype.hasOwnProperty;let s=new WeakMap,a=()=>{},i=a(),c=Object,l=e=>e===i,u=(e,t)=>({...e,...t}),f={},d={},g="undefined",h=typeof window!=g,p=typeof document!=g,y=h&&"Deno"in window,w=(e,t)=>{let r=s.get(e);return[()=>!l(t)&&e.get(t)||f,n=>{if(!l(t)){let o=e.get(t);t in d||(d[t]=o),r[5](t,u(o,n),o||f)}},r[6],()=>!l(t)&&t in d?d[t]:!l(t)&&e.get(t)||f]},S=!0,[m,E]=h&&window.addEventListener?[window.addEventListener.bind(window),window.removeEventListener.bind(window)]:[a,a],$={initFocus:e=>(p&&document.addEventListener("visibilitychange",e),m("focus",e),()=>{p&&document.removeEventListener("visibilitychange",e),E("focus",e)}),initReconnect:e=>{let t=()=>{S=!0,e()},r=()=>{S=!1};return m("online",t),m("offline",r),()=>{E("online",t),E("offline",r)}}},v=!r.default.useId,T=!h||y,b=T?r.useEffect:r.useLayoutEffect,R="u">typeof navigator&&navigator.connection,A=!T&&R&&(["slow-2g","2g"].includes(R.effectiveType)||R.saveData),O=new WeakMap,k=(e,t)=>e===`[object ${t}]`,j=0,_=e=>{let t,r,n=typeof e,o=c.prototype.toString.call(e),s=k(o,"Date"),a=k(o,"RegExp"),i=k(o,"Object");if(c(e)!==e||s||a)t=s?e.toJSON():"symbol"==n?e.toString():"string"==n?JSON.stringify(e):""+e;else{if(t=O.get(e))return t;if(t=++j+"~",O.set(e,t),Array.isArray(e)){for(r=0,t="@";r<e.length;r++)t+=_(e[r])+",";O.set(e,t)}if(i){t="#";let n=c.keys(e).sort();for(;!l(r=n.pop());)l(e[r])||(t+=r+":"+_(e[r])+",");O.set(e,t)}}return t},N=e=>{if("function"==typeof e)try{e=e()}catch(t){e=""}let t=e;return[e="string"==typeof e?e:(Array.isArray(e)?e.length:e)?_(e):"",t]},C=0,I=()=>++C;async function L(...e){let[t,r,n,o]=e,a=u({populateCache:!0,throwOnError:!0},"boolean"==typeof o?{revalidate:o}:o||{}),c=a.populateCache,f=a.rollbackOnError,d=a.optimisticData,g=a.throwOnError;if("function"==typeof r){let e=[];for(let n of t.keys())!/^\$(inf|sub)\$/.test(n)&&r(t.get(n)._k)&&e.push(n);return Promise.all(e.map(h))}return h(r);async function h(r){let o,[u]=N(r);if(!u)return;let[h,p]=w(t,u),[y,S,m,E]=s.get(t),$=()=>{let e=y[u];return("function"==typeof a.revalidate?a.revalidate(h().data,r):!1!==a.revalidate)&&(delete m[u],delete E[u],e&&e[0])?e[0](2).then(()=>h().data):h().data};if(e.length<3)return $();let v=n,T=!1,b=I();S[u]=[b,0];let R=!l(d),A=h(),O=A.data,k=A._c,j=l(k)?O:k;if(R&&p({data:d="function"==typeof d?d(j,O):d,_c:j}),"function"==typeof v)try{v=v(j)}catch(e){o=e,T=!0}if(v&&"function"==typeof v.then){let e;if(v=await v.catch(e=>{o=e,T=!0}),b!==S[u][0]){if(T)throw o;return v}T&&R&&(e=o,"function"==typeof f?f(e):!1!==f)&&(c=!0,p({data:j,_c:i}))}if(c&&!T&&("function"==typeof c?p({data:c(v,j),error:i,_c:i}):p({data:v,error:i,_c:i})),S[u][1]=I(),Promise.resolve($()).then(()=>{p({_c:i})}),T){if(g)throw o;return}return v}}let M=(e,t)=>{for(let r in e)e[r][0]&&e[r][0](t)},D=(e,t)=>{if(!s.has(e)){let r=u($,t),n=Object.create(null),o=L.bind(i,e),c=a,l=Object.create(null),f=(e,t)=>{let r=l[e]||[];return l[e]=r,r.push(t),()=>r.splice(r.indexOf(t),1)},d=(t,r,n)=>{e.set(t,r);let o=l[t];if(o)for(let e of o)e(r,n)},g=()=>{if(!s.has(e)&&(s.set(e,[n,Object.create(null),Object.create(null),Object.create(null),o,d,f]),!T)){let t=r.initFocus(setTimeout.bind(i,M.bind(i,n,0))),o=r.initReconnect(setTimeout.bind(i,M.bind(i,n,1)));c=()=>{t&&t(),o&&o(),s.delete(e)}}};return g(),[e,o,g,c]}return[e,s.get(e)[4]]},[P,V]=D(new Map),x=u({onLoadingSlow:a,onSuccess:a,onError:a,onErrorRetry:(e,t,r,n,o)=>{let s=r.errorRetryCount,a=o.retryCount,i=~~((Math.random()+.5)*(1<<(a<8?a:8)))*r.errorRetryInterval;(l(s)||!(a>s))&&setTimeout(n,i,o)},onDiscarded:a,revalidateOnFocus:!0,revalidateOnReconnect:!0,revalidateIfStale:!0,shouldRetryOnError:!0,errorRetryInterval:A?1e4:5e3,focusThrottleInterval:5e3,dedupingInterval:2e3,loadingTimeout:A?5e3:3e3,compare:function e(t,r){var n,s;if(t===r)return!0;if(t&&r&&(n=t.constructor)===r.constructor){if(n===Date)return t.getTime()===r.getTime();if(n===RegExp)return t.toString()===r.toString();if(n===Array){if((s=t.length)===r.length)for(;s--&&e(t[s],r[s]););return -1===s}if(!n||"object"==typeof t){for(n in s=0,t)if(o.call(t,n)&&++s&&!o.call(r,n)||!(n in r)||!e(t[n],r[n]))return!1;return Object.keys(r).length===s}}return t!=t&&r!=r},isPaused:()=>!1,cache:P,mutate:V,fallback:{}},{isOnline:()=>S,isVisible:()=>{let e=p&&document.visibilityState;return l(e)||"hidden"!==e}}),F=(e,t)=>{let r=u(e,t);if(t){let{use:n,fallback:o}=e,{use:s,fallback:a}=t;n&&s&&(r.use=n.concat(s)),o&&a&&(r.fallback=u(o,a))}return r},J=(0,r.createContext)({});var U=e.i(786254);let W=h&&window.__SWR_DEVTOOLS_USE__,B=(W?window.__SWR_DEVTOOLS_USE__:[]).concat(e=>(t,r,n)=>{let o=r&&((...e)=>{let[n]=N(t),[,,,o]=s.get(P);if(n.startsWith("$inf$"))return r(...e);let a=o[n];return l(a)?r(...e):(delete o[n],a)});return e(t,o,n)});W&&(window.__SWR_DEVTOOLS_REACT__=r.default);let q=()=>{},z=q(),K=(new WeakMap,r.default.use||(e=>{switch(e.status){case"pending":throw e;case"fulfilled":return e.value;case"rejected":throw e.reason;default:throw e.status="pending",e.then(t=>{e.status="fulfilled",e.value=t},t=>{e.status="rejected",e.reason=t}),e}})),H={dedupe:!0},Q=Promise.resolve(i);c.defineProperty(e=>{let{value:t}=e,n=(0,r.useContext)(J),o="function"==typeof t,s=(0,r.useMemo)(()=>o?t(n):t,[o,n,t]),a=(0,r.useMemo)(()=>o?s:F(n,s),[o,n,s]),c=s&&s.provider,l=(0,r.useRef)(i);c&&!l.current&&(l.current=D(c(a.cache||P),s));let f=l.current;return f&&(a.cache=f[0],a.mutate=f[1]),b(()=>{if(f)return f[2]&&f[2](),f[3]},[]),(0,r.createElement)(J.Provider,u(e,{value:a}))},"defaultValue",{value:x});let Y=(t=(e,t,o)=>{let{cache:c,compare:f,suspense:d,fallbackData:p,revalidateOnMount:y,revalidateIfStale:S,refreshInterval:m,refreshWhenHidden:E,refreshWhenOffline:$,keepPreviousData:R,strictServerPrefetchWarning:A}=o,[O,k,j,_]=s.get(c),[C,M]=N(e),D=(0,r.useRef)(!1),P=(0,r.useRef)(!1),V=(0,r.useRef)(C),x=(0,r.useRef)(t),F=(0,r.useRef)(o),J=()=>F.current.isVisible()&&F.current.isOnline(),[W,B,q,z]=w(c,C),Y=(0,r.useRef)({}).current,G=l(p)?l(o.fallback)?i:o.fallback[C]:p,X=(e,t)=>{for(let r in Y)if("data"===r){if(!f(e[r],t[r])&&(!l(e[r])||!f(ei,t[r])))return!1}else if(t[r]!==e[r])return!1;return!0},Z=(0,r.useMemo)(()=>{let e=!!C&&!!t&&(l(y)?!F.current.isPaused()&&!d&&!1!==S:y),r=t=>{let r=u(t);return(delete r._k,e)?{isValidating:!0,isLoading:!0,...r}:r},n=W(),o=z(),s=r(n),a=n===o?s:r(o),i=s;return[()=>{let e=r(W());return X(e,i)?(i.data=e.data,i.isLoading=e.isLoading,i.isValidating=e.isValidating,i.error=e.error,i):(i=e,e)},()=>a]},[c,C]),ee=(0,n.useSyncExternalStore)((0,r.useCallback)(e=>q(C,(t,r)=>{X(r,t)||e()}),[c,C]),Z[0],Z[1]),et=!D.current,er=O[C]&&O[C].length>0,en=ee.data,eo=l(en)?G&&"function"==typeof G.then?K(G):G:en,es=ee.error,ea=(0,r.useRef)(eo),ei=R?l(en)?l(ea.current)?eo:ea.current:en:eo,ec=C&&l(eo),el=!T&&(0,n.useSyncExternalStore)(()=>a,()=>!1,()=>!0);A&&el&&!d&&ec&&console.warn(`Missing pre-initiated data for serialized key "${C}" during server-side rendering. Data fethcing should be initiated on the server and provided to SWR via fallback data. You can set "strictServerPrefetchWarning: false" to disable this warning.`);let eu=(!er||!!l(es))&&(et&&!l(y)?y:!F.current.isPaused()&&(d?!l(eo)&&S:l(eo)||S)),ef=!!(C&&t&&et&&eu),ed=l(ee.isValidating)?ef:ee.isValidating,eg=l(ee.isLoading)?ef:ee.isLoading,eh=(0,r.useCallback)(async e=>{let t,r,n=x.current;if(!C||!n||P.current||F.current.isPaused())return!1;let s=!0,a=e||{},c=!j[C]||!a.dedupe,u=()=>v?!P.current&&C===V.current&&D.current:C===V.current,d={isValidating:!1,isLoading:!1},g=()=>{B(d)},h=()=>{let e=j[C];e&&e[1]===r&&delete j[C]},p={isValidating:!0};l(W().data)&&(p.isLoading=!0);try{if(c&&(B(p),o.loadingTimeout&&l(W().data)&&setTimeout(()=>{s&&u()&&F.current.onLoadingSlow(C,o)},o.loadingTimeout),j[C]=[n(M),I()]),[t,r]=j[C],t=await t,c&&setTimeout(h,o.dedupingInterval),!j[C]||j[C][1]!==r)return c&&u()&&F.current.onDiscarded(C),!1;d.error=i;let e=k[C];if(!l(e)&&(r<=e[0]||r<=e[1]||0===e[1]))return g(),c&&u()&&F.current.onDiscarded(C),!1;let a=W().data;d.data=f(a,t)?a:t,c&&u()&&F.current.onSuccess(t,C,o)}catch(r){h();let e=F.current,{shouldRetryOnError:t}=e;!e.isPaused()&&(d.error=r,c&&u())&&(e.onError(r,C,e),(!0===t||"function"==typeof t&&t(r))&&(!F.current.revalidateOnFocus||!F.current.revalidateOnReconnect||J())&&e.onErrorRetry(r,C,e,e=>{let t=O[C];t&&t[0]&&t[0](U.ERROR_REVALIDATE_EVENT,e)},{retryCount:(a.retryCount||0)+1,dedupe:!0}))}return s=!1,g(),!0},[C,c]),ep=(0,r.useCallback)((...e)=>L(c,V.current,...e),[]);if(b(()=>{x.current=t,F.current=o,l(en)||(ea.current=en)}),b(()=>{var e;let t;if(!C)return;let r=eh.bind(i,H),n=0;F.current.revalidateOnFocus&&(n=Date.now()+F.current.focusThrottleInterval);let o=(e=(e,t={})=>{if(e==U.FOCUS_EVENT){let e=Date.now();F.current.revalidateOnFocus&&e>n&&J()&&(n=e+F.current.focusThrottleInterval,r())}else if(e==U.RECONNECT_EVENT)F.current.revalidateOnReconnect&&J()&&r();else if(e==U.MUTATE_EVENT)return eh();else if(e==U.ERROR_REVALIDATE_EVENT)return eh(t)},(t=O[C]||(O[C]=[])).push(e),()=>{let r=t.indexOf(e);r>=0&&(t[r]=t[t.length-1],t.pop())});if(P.current=!1,V.current=C,D.current=!0,B({_k:M}),eu&&!j[C])if(l(eo)||T)r();else h&&typeof window.requestAnimationFrame!=g?window.requestAnimationFrame(r):setTimeout(r,1);return()=>{P.current=!0,o()}},[C]),b(()=>{let e;function t(){let t="function"==typeof m?m(W().data):m;t&&-1!==e&&(e=setTimeout(r,t))}function r(){!W().error&&(E||F.current.isVisible())&&($||F.current.isOnline())?eh(H).then(t):t()}return t(),()=>{e&&(clearTimeout(e),e=-1)}},[m,E,$,C]),(0,r.useDebugValue)(ei),d){if(!v&&T&&ec)throw Error("Fallback data is required when using Suspense in SSR.");ec&&(x.current=t,F.current=o,P.current=!1);let e=_[C];if(K(!l(e)&&ec?ep(e):Q),!l(es)&&ec)throw es;let r=ec?eh(H):Q;!l(ei)&&ec&&(r.status="fulfilled",r.value=!0),K(r)}return{mutate:ep,get data(){return Y.data=!0,ei},get error(){return Y.error=!0,es},get isValidating(){return Y.isValidating=!0,ed},get isLoading(){return Y.isLoading=!0,eg}}},function(...e){let n,o=(n=(0,r.useContext)(J),(0,r.useMemo)(()=>u(x,n),[n])),[s,a,i]="function"==typeof e[1]?[e[0],e[1],e[2]||{}]:[e[0],null,(null===e[1]?e[2]:e[1])||{}],c=F(o,i),l=t,{use:f}=c,d=(f||[]).concat(B);for(let e=d.length;e--;)l=d[e](l);return l(s,a||c.fetcher||null,c)});e.s(["default",()=>Y],262189),e.s(["mutate",()=>V],103393)},300739,e=>{"use strict";var t=e.i(262189),r=e.i(103393),n=e.i(524741);let o="/api/sessions";function s(){let{data:e=[],error:r,isLoading:s}=(0,t.default)(o,n.getSessions,{refreshInterval:2e3,revalidateOnFocus:!0});return{sessions:e,isLoading:s,error:r}}function a(){return(0,r.mutate)(o)}e.s(["mutateSessions",()=>a,"useSessions",()=>s])}]);
@@ -118,6 +118,9 @@ import {
118
118
  getSessionFiles,
119
119
  getBrowserStreamStatus,
120
120
  getBrowserRecordings,
121
+ getSession,
122
+ getSessionDiff,
123
+ getSessionAttachments,
121
124
  type SessionFile as ApiSessionFile,
122
125
  type BrowserRecordingInfo,
123
126
  } from '@/lib/api';
@@ -132,7 +135,7 @@ import {
132
135
  SelectTrigger,
133
136
  SelectValue,
134
137
  } from '@/components/ui/select';
135
- import { MessageSquare, Copy, RefreshCw, AlertTriangle, Terminal as TerminalIcon, FileCode, Radio, Pencil, Check, Settings, RotateCcw, FolderOpen, PanelLeft, FileIcon, Download, X, ChevronDown, ChevronUp, Play, ArrowUp, Trash2, Monitor, ListChecks, MoreVertical, Globe, Video, ImageIcon } from 'lucide-react';
138
+ import { MessageSquare, Copy, RefreshCw, AlertTriangle, Terminal as TerminalIcon, FileCode, Radio, Pencil, Check, Settings, RotateCcw, FolderOpen, PanelLeft, FileIcon, Download, X, ChevronDown, ChevronUp, Play, ArrowUp, Trash2, Monitor, ListChecks, MoreVertical, Globe, Video, ImageIcon, Bug } from 'lucide-react';
136
139
  import { useSidebar } from '@/components/ui/sidebar';
137
140
  import {
138
141
  Dialog,
@@ -541,6 +544,80 @@ interface QueuedMessage {
541
544
  selectedElements?: string | null;
542
545
  }
543
546
 
547
+ /**
548
+ * Gather EVERYTHING a developer might want to debug a session and return it
549
+ * as a single pretty-printed JSON blob suitable for pasting into a bug
550
+ * report. Every field is best-effort — anything that fails to fetch is
551
+ * recorded as `{ __error: '...' }` in its slot so the rest of the dump
552
+ * still arrives.
553
+ *
554
+ * Intentionally chunky: messages include full model-message content,
555
+ * tool inputs/outputs, attachments, todos, plans, checkpoints, working-
556
+ * directory diff, pending approvals, browser recordings, etc. The whole
557
+ * point is "everything we could possibly need."
558
+ */
559
+ async function buildSessionDebugDump(sessionId: string): Promise<string> {
560
+ const safe = async <T,>(label: string, fn: () => Promise<T>): Promise<T | { __error: string }> => {
561
+ try {
562
+ return await fn();
563
+ } catch (err: any) {
564
+ return { __error: err?.message ?? String(err) };
565
+ }
566
+ };
567
+
568
+ const [
569
+ session,
570
+ messages,
571
+ todos,
572
+ plans,
573
+ checkpoints,
574
+ diff,
575
+ attachments,
576
+ files,
577
+ pendingApprovals,
578
+ pendingInput,
579
+ browserStream,
580
+ browserRecordings,
581
+ ] = await Promise.all([
582
+ safe('session', () => getSession(sessionId)),
583
+ safe('messages', () => getSessionMessages(sessionId)),
584
+ safe('todos', () => getSessionTodos(sessionId)),
585
+ safe('plans', () => getSessionPlans(sessionId)),
586
+ safe('checkpoints', () => getSessionCheckpoints(sessionId)),
587
+ safe('diff', () => getSessionDiff(sessionId)),
588
+ safe('attachments', () => getSessionAttachments(sessionId)),
589
+ safe('files', () => getSessionFiles(sessionId)),
590
+ safe('pendingApprovals', () => getPendingApprovals(sessionId)),
591
+ safe('pendingInput', () => getPendingInput(sessionId)),
592
+ safe('browserStream', () => getBrowserStreamStatus(sessionId)),
593
+ safe('browserRecordings', () => getBrowserRecordings(sessionId)),
594
+ ]);
595
+
596
+ const dump = {
597
+ __meta: {
598
+ kind: 'sparkecoder-session-debug-dump',
599
+ version: 1,
600
+ capturedAt: new Date().toISOString(),
601
+ sessionId,
602
+ origin: typeof window !== 'undefined' ? window.location.origin : null,
603
+ userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : null,
604
+ },
605
+ session,
606
+ messages,
607
+ todos,
608
+ plans,
609
+ checkpoints,
610
+ diff,
611
+ attachments,
612
+ files,
613
+ pendingApprovals,
614
+ pendingInput,
615
+ browserStream,
616
+ browserRecordings,
617
+ };
618
+ return JSON.stringify(dump, null, 2);
619
+ }
620
+
544
621
  export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps) {
545
622
  const { toggleSidebar } = useSidebar();
546
623
  const [chatItems, setChatItems] = useState<ChatItem[]>([]);
@@ -622,6 +699,7 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
622
699
  const [sessionFiles, setSessionFiles] = useState<ApiSessionFile[]>([]);
623
700
  const [browserRecordingsOpen, setBrowserRecordingsOpen] = useState(false);
624
701
  const [browserRecordings, setBrowserRecordings] = useState<BrowserRecordingInfo[]>([]);
702
+ const [copyDebugState, setCopyDebugState] = useState<'idle' | 'copying' | 'copied' | 'error'>('idle');
625
703
 
626
704
  // Load config for available models
627
705
  useEffect(() => {
@@ -2839,6 +2917,39 @@ export function ChatInterface({ session, isEmbed = false }: ChatInterfaceProps)
2839
2917
  <Video className="size-4 mr-2" />
2840
2918
  Browser Recordings
2841
2919
  </DropdownMenuItem>
2920
+ <DropdownMenuItem
2921
+ onClick={async () => {
2922
+ setCopyDebugState('copying');
2923
+ try {
2924
+ const json = await buildSessionDebugDump(session.id);
2925
+ await navigator.clipboard.writeText(json);
2926
+ setCopyDebugState('copied');
2927
+ console.log(`[debug-dump] copied ${json.length.toLocaleString()} chars to clipboard for session ${session.id}`);
2928
+ setTimeout(() => setCopyDebugState('idle'), 2500);
2929
+ } catch (err) {
2930
+ console.error('[debug-dump] failed:', err);
2931
+ setCopyDebugState('error');
2932
+ setTimeout(() => setCopyDebugState('idle'), 2500);
2933
+ }
2934
+ }}
2935
+ >
2936
+ {copyDebugState === 'copying' ? (
2937
+ <RefreshCw className="size-4 mr-2 animate-spin" />
2938
+ ) : copyDebugState === 'copied' ? (
2939
+ <Check className="size-4 mr-2 text-green-500" />
2940
+ ) : copyDebugState === 'error' ? (
2941
+ <AlertTriangle className="size-4 mr-2 text-red-500" />
2942
+ ) : (
2943
+ <Bug className="size-4 mr-2" />
2944
+ )}
2945
+ {copyDebugState === 'copying'
2946
+ ? 'Gathering…'
2947
+ : copyDebugState === 'copied'
2948
+ ? 'Copied to clipboard'
2949
+ : copyDebugState === 'error'
2950
+ ? 'Failed — see console'
2951
+ : 'Copy debug JSON'}
2952
+ </DropdownMenuItem>
2842
2953
  </DropdownMenuContent>
2843
2954
  </DropdownMenu>
2844
2955
  </div>