pikiloom 0.4.54 → 0.4.55

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 (25) hide show
  1. package/dashboard/dist/assets/{AgentTab-DJfd-aVj.js → AgentTab-CvghrxSK.js} +1 -1
  2. package/dashboard/dist/assets/{ConnectionModal-DyybJ5vd.js → ConnectionModal-DHw2M8z4.js} +1 -1
  3. package/dashboard/dist/assets/{DirBrowser-BeFl4pxe.js → DirBrowser-hui6YZ3V.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-VUqUXTUU.js → ExtensionsTab-DAqHco47.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-x5c4G5ck.js → IMAccessTab-BFKhRG8w.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-CVov70Z-.js → Modal-oPg6drTY.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-Cru4ScU4.js → Modals-DwMaDCJc.js} +1 -1
  8. package/dashboard/dist/assets/{Select-D39-GxSd.js → Select-Dlz8-JdF.js} +1 -1
  9. package/dashboard/dist/assets/SessionPanel-DPb40DDF.js +1 -0
  10. package/dashboard/dist/assets/{SystemTab-C1SNkUop.js → SystemTab-CzexSRMC.js} +1 -1
  11. package/dashboard/dist/assets/{index-89WIHUvA.js → index-BIX62DbC.js} +2 -2
  12. package/dashboard/dist/assets/{index-BTlkZmgz.js → index-CRE3Clam.js} +3 -3
  13. package/dashboard/dist/assets/{shared-CpIdR9QI.js → shared-BATHuwRO.js} +1 -1
  14. package/dashboard/dist/index.html +1 -1
  15. package/dist/agent/drivers/claude.js +11 -37
  16. package/dist/agent/index.js +1 -1
  17. package/dist/agent/kernel-bridge.js +6 -1
  18. package/dist/agent/utils.js +33 -0
  19. package/dist/bot/commands.js +4 -2
  20. package/package.json +1 -1
  21. package/packages/kernel/dist/drivers/claude.d.ts +3 -0
  22. package/packages/kernel/dist/drivers/claude.js +53 -4
  23. package/packages/kernel/dist/index.d.ts +1 -1
  24. package/packages/kernel/dist/index.js +1 -1
  25. package/dashboard/dist/assets/SessionPanel-CryozDR-.js +0 -1
@@ -59,6 +59,8 @@ export class ClaudeDriver {
59
59
  // Dangling-tool-loop tracking: sawToolResult flips on the first tool_result; textSinceToolResult
60
60
  // goes false at every tool_result and true again once the model streams visible text.
61
61
  sawToolResult: false, textSinceToolResult: false,
62
+ // Wall-clock of the last parsed stream event — the hold cap defers while this is fresh.
63
+ lastEventAt: Date.now(),
62
64
  sessionId: null, model: null,
63
65
  stopReason: null, error: null,
64
66
  input: null, output: null, cached: null,
@@ -138,13 +140,29 @@ export class ClaudeDriver {
138
140
  ok: opts.ok ?? !state.error, text: state.text, reasoning: state.reasoning || undefined,
139
141
  error: state.error, stopReason: opts.stopReason ?? state.stopReason, sessionId: state.sessionId, usage: usageOf(),
140
142
  }, opts.kill ?? true);
141
- // Absolute cap while holding for a still-running background task (stopReason marks it as
142
- // "still running in the background" so the empty-text fallback reads right). Idempotent.
143
+ // Cap while holding for a still-running background task (stopReason marks it as
144
+ // "still running in the background" so the terminal presentation reads right). Idempotent
145
+ // the countdown is absolute from the first arm. Sub-agent-backed holds use the longer
146
+ // agent cap, and a cap that fires while events are still flowing defers instead of
147
+ // cutting an actively-working turn mid-generation (the 2026-07-06 "停止不再继续生成":
148
+ // the 10-min cap yanked a live research turn 22s after its last tool_result and the
149
+ // graceful-close leak guard then killed the 4 still-running Explore agents).
143
150
  const armHoldCap = () => {
144
151
  if (holdCapTimer)
145
152
  return;
146
- holdCapTimer = setTimeout(() => { if (!settled)
147
- settleResult({ stopReason: 'background', kill: false }); }, claudeBgHoldCapMs());
153
+ const capMs = claudeTurnHasAgentBackground(state) ? claudeBgAgentHoldCapMs() : claudeBgHoldCapMs();
154
+ const fire = () => {
155
+ holdCapTimer = null;
156
+ if (settled)
157
+ return;
158
+ if (Date.now() - (state.lastEventAt ?? 0) < claudeBgSettleQuietMs()) {
159
+ holdCapTimer = setTimeout(fire, claudeBgHoldRecheckMs());
160
+ unref(holdCapTimer);
161
+ return;
162
+ }
163
+ settleResult({ stopReason: 'background', kill: false });
164
+ };
165
+ holdCapTimer = setTimeout(fire, capMs);
148
166
  unref(holdCapTimer);
149
167
  };
150
168
  // Grace close once all background work is done: settle gracefully (no kill) if Claude stays
@@ -225,6 +243,7 @@ export class ClaudeDriver {
225
243
  catch {
226
244
  continue;
227
245
  }
246
+ state.lastEventAt = Date.now();
228
247
  handleClaudeEvent(ev, state, ctx.emit);
229
248
  const pending = pendingClaudeBackgroundTasks(state);
230
249
  // Any model output means the model is alive and streaming — cancel the post-tool stall watchdog.
@@ -689,6 +708,27 @@ export function claudeBgHoldCapMs() {
689
708
  const raw = Number(process.env.PIKILOOM_CLAUDE_BG_HOLD_MS);
690
709
  return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_HOLD_CAP_DEFAULT_MS;
691
710
  }
711
+ // Sub-agent-backed background work (Task/Agent tool launches) gets a much longer hold: these
712
+ // are finite model-driven jobs whose results the turn is genuinely waiting on — a research
713
+ // fleet mapping two repos legitimately runs past the 10-minute daemon cap, and capping it
714
+ // there discarded the agents' work and cut the turn mid-flight ("停止不再继续生成").
715
+ // Override with PIKILOOM_CLAUDE_BG_AGENT_HOLD_MS.
716
+ const CLAUDE_BG_AGENT_HOLD_CAP_DEFAULT_MS = 45 * 60_000;
717
+ export function claudeBgAgentHoldCapMs() {
718
+ const raw = Number(process.env.PIKILOOM_CLAUDE_BG_AGENT_HOLD_MS);
719
+ return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_AGENT_HOLD_CAP_DEFAULT_MS;
720
+ }
721
+ export function claudeTurnHasAgentBackground(s) {
722
+ return !!s?.bgAgentTasks?.size;
723
+ }
724
+ // When the hold cap fires while events are still flowing, defer and re-check on this cadence
725
+ // instead of yanking an actively-working turn (the cap bounds SILENT stuck holds, nothing else).
726
+ // Override with PIKILOOM_CLAUDE_BG_HOLD_RECHECK_MS (tests need sub-second rechecks).
727
+ const CLAUDE_BG_HOLD_RECHECK_DEFAULT_MS = 30_000;
728
+ export function claudeBgHoldRecheckMs() {
729
+ const raw = Number(process.env.PIKILOOM_CLAUDE_BG_HOLD_RECHECK_MS);
730
+ return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_HOLD_RECHECK_DEFAULT_MS;
731
+ }
692
732
  // Once every KNOWN background task has finished, how long Claude must stay quiet (no further
693
733
  // output at all) before we close a background turn. A completed task's status races AHEAD of the
694
734
  // wake-up turn that reports it — with N parallel agents finishing together, the last agent's
@@ -743,6 +783,15 @@ export function trackClaudeBackgroundTask(ev, s) {
743
783
  const subtype = ev?.subtype;
744
784
  if (subtype !== 'task_started' && subtype !== 'task_updated' && subtype !== 'task_notification')
745
785
  return;
786
+ // Sub-agent-backed background tasks (Task/Agent tool launches) are FINITE model-driven jobs,
787
+ // unlike a detached shell that may daemonize forever — they earn a much longer hold cap
788
+ // (see armHoldCap). The task_started's tool_use_id points at the launching tool call, which
789
+ // for sub-agents lives in s.subAgents.
790
+ if (subtype === 'task_started') {
791
+ const tui = String(ev?.tool_use_id ?? '').trim();
792
+ if (tui && s?.subAgents?.has?.(tui))
793
+ (s.bgAgentTasks ||= new Set()).add(String(ev?.task_id ?? ev?.id ?? tui));
794
+ }
746
795
  const id = String(ev?.task_id ?? ev?.tool_use_id ?? '').trim();
747
796
  if (!id)
748
797
  return;
@@ -9,7 +9,7 @@ export type { SessionStore, CoreSessionRecord, ModelResolver, ModelInjection, To
9
9
  export type { LoomIO, PromptInput, Surface, SurfaceCapabilities, Plugin, SpawnContribution, } from './contracts/surface.js';
10
10
  export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
11
11
  export { EchoDriver } from './drivers/echo.js';
12
- export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
12
+ export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
13
13
  export { CodexDriver } from './drivers/codex.js';
14
14
  export { GeminiDriver } from './drivers/gemini.js';
15
15
  export { AcpDriver, type AcpDriverConfig } from './drivers/acp.js';
@@ -19,7 +19,7 @@ export { attachTui } from './runtime/tui.js';
19
19
  export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
20
20
  // Drivers & surfaces (also available via subpath exports)
21
21
  export { EchoDriver } from './drivers/echo.js';
22
- export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
22
+ export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgAgentHoldCapMs, claudeTurnHasAgentBackground, claudeBgHoldRecheckMs, claudeBgSettleQuietMs, claudeModelStallMs, claudeUserEventHasToolResult, handleClaudeEvent, claudeTurnEndedDangling, claudeTruncatedRecoveryEnabled, CLAUDE_TRUNCATED_RECOVERY_PROMPT, } from './drivers/claude.js';
23
23
  export { CodexDriver } from './drivers/codex.js';
24
24
  export { GeminiDriver } from './drivers/gemini.js';
25
25
  export { AcpDriver } from './drivers/acp.js';
@@ -1 +0,0 @@
1
- import{r,j as s}from"./react-vendor-C7Sl8SE7.js";import{c as Jt,I as Zt,S as Be,m as _e,a as I,u as ut,d as er,g as tr,l as rr,e as nr,j as sr,f as lr,s as or,Y as ar}from"./index-89WIHUvA.js";import{s as ur,l as cr,n as jt,m as ir,a as dr,o as fr,d as mr,b as gr,p as pr,c as De,e as hr,f as wt,g as Fe,h as ct,i as xr,T as kr,R as Sr,U as Nt,j as At,k as br,L as vr,I as Ir}from"./index-BTlkZmgz.js";import{M as Ot,a as Et}from"./Modal-CVov70Z-.js";import"./router-DHISdpPk.js";import"./Select-D39-GxSd.js";import"./DirBrowser-BeFl4pxe.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-VUqUXTUU.js";function yr({snapshot:o}){const[t,f]=r.useState(o.currentIndex??0),[w,y]=r.useState(""),[x,g]=r.useState(!1),[C,S]=r.useState(null);r.useEffect(()=>{f(o.currentIndex??0),y(""),S(null)},[o.promptId,o.currentIndex]);const O=o.questions||[],T=O[t]||null,J=O.length,q=!!(T?.options&&T.options.length),E=q?!!T?.allowFreeform:!0,ue=a=>{a&&(f(i=>i+1),y(""))},ye=async a=>{if(!x){g(!0),S(null);try{const i=await I.interactionSelectOption(o.promptId,a);if(!i.ok){S(i.error||"Failed to submit selection.");return}ue(i.advanced)}catch(i){S(i?.message||"Network error.")}finally{g(!1)}}},ce=async()=>{if(x)return;const a=w.trim();if(!a&&!T?.allowEmpty){S("Please enter a response.");return}g(!0),S(null);try{const i=await I.interactionSubmitText(o.promptId,a);if(!i.ok){S(i.error||"Failed to submit answer.");return}ue(i.advanced)}catch(i){S(i?.message||"Network error.")}finally{g(!1)}},p=async()=>{if(!x){g(!0),S(null);try{const a=await I.interactionSkip(o.promptId);if(!a.ok){S(a.error||"Failed to skip.");return}ue(a.advanced)}catch(a){S(a?.message||"Network error.")}finally{g(!1)}}},ie=async()=>{if(!x){g(!0);try{await I.interactionCancel(o.promptId)}catch{}}},de=r.useMemo(()=>{const a=[];return o.hint&&a.push(o.hint),J>1&&a.push(`Question ${t+1} of ${J}`),a.join(" · ")||void 0},[o.hint,t,J]);return s.jsxs(Ot,{open:!0,onClose:ie,wide:q&&(T?.options?.length||0)>3,children:[s.jsx(Et,{title:o.title||"Pikiloom needs your input",description:de,onClose:ie}),T?s.jsxs("div",{className:"space-y-4",children:[s.jsxs("div",{children:[s.jsx("div",{className:"text-xs font-medium uppercase tracking-wide text-fg-5",children:T.header||"Question"}),s.jsx("div",{className:"mt-1 whitespace-pre-wrap text-sm leading-relaxed text-fg",children:T.prompt})]}),q&&s.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-2",children:(T.options||[]).map(a=>s.jsxs("button",{type:"button",disabled:x,onClick:()=>ye(a.value||a.label),className:Jt("group rounded-lg border border-edge bg-panel-alt px-3 py-2 text-left text-sm transition","hover:border-control-border-h hover:bg-control-h hover:shadow-sm","focus:outline-none focus:ring-2 focus:ring-[var(--th-glow-a)]","disabled:cursor-not-allowed disabled:opacity-50"),children:[s.jsx("div",{className:"font-medium text-fg group-hover:text-fg",children:a.label}),a.description&&s.jsx("div",{className:"mt-0.5 text-xs leading-snug text-fg-4",children:a.description})]},a.value||a.label))}),E&&s.jsx("div",{children:s.jsx(Zt,{value:w,onChange:a=>y(a.target.value),onKeyDown:a=>{a.key==="Enter"&&!a.shiftKey&&!x&&(a.preventDefault(),ce())},placeholder:q?"Or type a custom answer…":"Type your answer…",disabled:x,autoFocus:!q})}),C&&s.jsx("div",{className:"rounded-md border border-red-300/40 bg-red-500/10 px-3 py-2 text-xs text-red-600",children:C}),s.jsxs("div",{className:"flex items-center justify-between gap-3",children:[s.jsx("div",{className:"text-xs text-fg-5",children:x?s.jsxs("span",{className:"inline-flex items-center gap-2",children:[s.jsx(Be,{})," Submitting…"]}):s.jsxs("span",{children:["Press ",s.jsx("kbd",{className:"rounded border border-edge bg-panel-alt px-1.5 py-0.5 text-[10px] uppercase",children:"Enter"})," to send"]})}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(_e,{variant:"ghost",size:"sm",onClick:p,disabled:x,children:"Skip"}),E&&s.jsx(_e,{variant:"primary",size:"sm",onClick:ce,disabled:x||!w.trim()&&!T.allowEmpty,children:"Submit"})]})]})]}):s.jsxs("div",{className:"py-6 text-center text-sm text-fg-5",children:[s.jsx(Be,{className:"mr-2 inline-block"})," Waiting for the agent…"]})]})}function Tr(o){return o.isNull?(o.localStreamPending||o.holdsActiveState)&&!o.holdExpired?"reject-null":"apply":(typeof o.updatedAt=="number"?o.updatedAt:0)<o.lastAppliedUpdatedAt?"reject-stale":"apply"}function Rr(o,t){return typeof t!="number"?o:t>o?t:o}function jr(o,t){return!o||!o.length?[]:t.size?o.filter(f=>!t.has(f)):o.slice()}function wr(o,t,f,w){if(!o.size)return o;const y=new Set(t);let x=!1;const g=new Map;for(const[C,S]of o){if(f-S>w||!y.has(C)){x=!0;continue}g.set(C,S)}return x?g:o}const it=12,Lt=160,Nr=96,Ar=[],Lr=[],Or=[],Er=20,Mr=6e4,Pr=15e3,Cr=7e3,ae=new Map;function qr(o,t){return`${o}:${t}`}function Ur(o,t){for(ae.delete(o),ae.set(o,t);ae.size>Er;)ae.delete(ae.keys().next().value)}const Yr=r.memo(function({session:t,workdir:f,active:w=!0,onSessionChange:y,initialPendingPrompt:x,initialPendingImageUrls:g,onPendingPromptConsumed:C}){const S=ut(e=>e.locale),O=ut(e=>e.agentStatus?.agents?.find(n=>n.agent===t.agent)??null),T=O?.selectedEffort??null,J=O?.selectedModel??null,q=ut(e=>e.modelLayer),E=t.profileId?q?.profiles.find(e=>e.id===t.profileId)??null:null,ue=E?q?.providers.find(e=>e.id===E.providerId)??null:null,ye=t.profileId===void 0?O?.byokProviderName??null:ue?.name??null,ce=t.profileId===void 0?O?.byokProfileName??null:E&&E.name.trim().toLowerCase()!==E.modelId.trim().toLowerCase()?E.name:null,p=r.useMemo(()=>er(S),[S]),ie=tr(t.agent||""),de=rr(t),a=!!x||!!(g&&g.length),[i,Qe]=r.useState(null),[$e,fe]=r.useState(!a),[me,dt]=r.useState(!1),[c,F]=r.useState(null),[B,ge]=r.useState(!1),[U,Ke]=r.useState(null),[Mt,ft]=r.useState(0),[Pt,ze]=r.useState(null),[H,Te]=r.useState([]),[Ct,Re]=r.useState([]),[pe,We]=r.useState([]),[h,Z]=r.useState(x||null),[M,ee]=r.useState(g||[]),[qt,te]=r.useState(null),_=r.useRef(null);_.current=qt;const mt=r.useRef(h);mt.current=h;const[je,D]=r.useState([]),we=r.useRef([]);we.current=je;const he=r.useRef(null),[Ut,gt]=r.useState(null),[Ne,xe]=r.useState(null),[ke,Ye]=r.useState(""),[Q,Ve]=r.useState(!1),Ht=!!O?.capabilities?.fork,Xe=r.useRef(null),L=r.useRef(g||[]),$=r.useRef(c),Ge=r.useRef(B);$.current=c,Ge.current=B;const Je=r.useRef(H);Je.current=H;const pt=r.useRef(U);pt.current=U;const K=r.useRef(null),Ae=r.useRef(null),z=r.useRef(!0),re=r.useRef(!1),Le=r.useRef(null),Ze=r.useRef(!1),P=r.useRef(a),ne=r.useRef(!1),W=r.useRef(!1),et=r.useRef(!1),tt=r.useRef(!1),Y=r.useRef(null),Oe=r.useRef(0),Se=r.useRef(Date.now()),be=r.useRef(new Map),ht=r.useRef({model:null,effort:null}),Dt=r.useCallback(e=>{ht.current=e},[]);r.useEffect(()=>{et.current||!a||(et.current=!0,x&&!h&&Z(x),g&&g.length&&!M.length&&(ee(g),L.current=g),fe(!1),ft(e=>e+1),C?.())},[a,C]);const N=r.useCallback(()=>{Z(null),ee(e=>{for(const n of e)URL.revokeObjectURL(n);return[]}),L.current=[],te(null)},[]),V=r.useCallback(()=>{D(e=>{if(!e.length)return e;for(const n of e)for(const l of n.imageUrls)URL.revokeObjectURL(l);return[]}),he.current=null},[]),xt=r.useCallback((e,n)=>{Se.current=Date.now();const l=ur({streaming:Ge.current,liveStreamPhase:$.current?.phase??null,streamPhase:pt.current,queuedTaskCount:Je.current.length,pendingQueuedCount:we.current.length}),u=n||[];if(l){const d=`local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;he.current=d,D(k=>[...k,{localId:d,taskId:null,prompt:e||"",imageUrls:u}]);return}$.current?.phase==="done"&&F(null);for(const d of L.current)URL.revokeObjectURL(d);he.current=null,Z(e||null),ee(u),L.current=u,te(null)},[]),Ft=r.useCallback(e=>{const n=he.current;if(n){he.current=null,D(l=>{const u=l.findIndex(k=>k.localId===n);if(u<0)return l;const d=l.slice();return d[u]={...d[u],taskId:e},d});return}te(e)},[]),Bt=r.useCallback(async()=>{if(!Ne)return;const e=ke.trim();if(e){Ve(!0);try{const n=await I.forkSession(f,t.agent||"",t.sessionId,Ne.atTurn,e,{});if(!n.ok||!n.sessionKey){Ve(!1);return}const[l,u]=n.sessionKey.split(":");xe(null),Ye(""),y?.({agent:l,sessionId:u,workdir:f})}finally{Ve(!1)}}},[Ne,ke,f,t.agent,t.sessionId,y]);Xe.current=Bt;const Ee=r.useCallback(async(e,n={})=>{try{const l=await cr({workdir:f,agent:t.agent||"",sessionId:t.sessionId,rich:!0,turnOffset:e.turnOffset,turnLimit:e.turnLimit,lastNTurns:e.lastNTurns},{force:n.force});return l.ok?jt(l):null}catch{return null}},[f,t.agent,t.sessionId]),R=r.useCallback(async({keepOlder:e,force:n=!1,scrollToBottom:l=!1})=>{const u=t.sessionId;if(Le.current===u)return!1;Le.current=u;try{const d=await Ee({turnOffset:0,turnLimit:it},{force:n});if(!d||t.sessionId!==u)return!1;if(l&&(re.current=!0),Qe(k=>!k||!e?d:ir(k,d)),fe(!1),ne.current&&(ne.current=!1,N()),W.current){const k=W.current;W.current=!1;const X=k!==!0?k.taskId:null;$.current&&(k===!0||$.current.taskId===X)&&F(null)}return!0}finally{Le.current===u&&(Le.current=null)}},[Ee,N,t.sessionId]),Me=r.useCallback(async()=>{if(!i?.hasOlder||Ze.current)return;const e=K.current;e&&(Ae.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop}),Ze.current=!0,dt(!0);try{const n=await Ee({turnOffset:Math.max(0,i.totalTurns-i.startTurn),turnLimit:it});n?Qe(l=>l?dr(l,n):n):Ae.current=null}finally{Ze.current=!1,dt(!1)}},[Ee,i]),Pe=r.useRef(null),se=r.useCallback((e,n="ws")=>{const l=Ge.current||Je.current.length>0,u=Date.now()-Se.current>Pr;if(Tr({updatedAt:e?.updatedAt,isNull:!e,lastAppliedUpdatedAt:Oe.current,localStreamPending:P.current,holdsActiveState:l,holdExpired:u})!=="apply")return;const k=!e&&u&&(P.current||l);if(e&&(Se.current=Date.now(),Oe.current=Rr(Oe.current,e.updatedAt)),e?.sessionId&&e.sessionId!==t.sessionId&&(tt.current=!0,ve.current=`${t.agent}:${e.sessionId}`,y?.({agent:t.agent||"",sessionId:e.sessionId,workdir:f})),!e){const m=Pe.current;ge(!1),k?(P.current=!1,ne.current=!0,W.current=!0,V(),R({keepOlder:!0,force:!0,scrollToBottom:z.current})):m==="streaming"?(ne.current=!0,W.current=!0,R({keepOlder:!0,force:!0,scrollToBottom:z.current})):F(null),k||(P.current&&m!=="streaming"?R({keepOlder:!0,force:!0}):(m==="done"&&(N(),V()),m!==null&&(P.current=!1))),ze(null),Ke(null),Te([]),Re([]),We([]),Pe.current=null;return}Ke(e.phase),ze(e.taskId||null);const X=[];e.taskId&&X.push(e.taskId),Array.isArray(e.queuedTaskIds)&&X.push(...e.queuedTaskIds),be.current=wr(be.current,X,Date.now(),Mr);const le=be.current,ot=jr(e.queuedTaskIds,le),Rt=(Array.isArray(e.queuedTasks)?e.queuedTasks:[]).filter(m=>!le.has(m.taskId));if(Te(ot.length?ot:Ar),Re(Rt.length?Rt:Lr),We(Array.isArray(e.interactions)&&e.interactions.length?e.interactions:Or),fr({pendingTaskId:_.current,streamTaskId:e.taskId||null,queuedTaskIds:e.queuedTaskIds})){const m=_.current,j=mt.current||"",v=L.current;D(b=>b.some(oe=>oe.taskId===m)?b:[...b,{localId:`demote-${m}`,taskId:m,prompt:j,imageUrls:v}]),Z(null),ee([]),L.current=[],te(null),_.current=null}if(e.phase==="streaming"){if(F({taskId:e.taskId||null,phase:"streaming",text:e.text||"",thinking:e.thinking||"",activity:e.activity,plan:e.plan??null,model:e.model??null,effort:e.effort??null,previewMeta:e.previewMeta??null,subAgents:e.previewMeta?.subAgents??null,generatingImages:e.previewMeta?.generatingImages??0,artifacts:e.artifacts??null,startedAt:typeof e.startedAt=="number"?e.startedAt:null,error:null,question:e.question??null,questionBlocks:e.questionBlocks??null}),ge(!0),e.taskId&&e.taskId!==_.current){const m=we.current,j=m.findIndex(v=>v.taskId===e.taskId);if(j>=0){const v=m[j];for(const b of L.current)URL.revokeObjectURL(b);Z(v.prompt||null),ee(v.imageUrls),L.current=v.imageUrls,te(e.taskId),D(b=>b.filter((oe,G)=>G!==j))}}z.current&&(re.current=!0)}else if(e.phase==="queued")F(null),ge(!1);else if(e.phase==="done"){const m=mr($.current?.taskId??null,e.taskId||null),j=ot.length>0;if(m){ge(!1),F(G=>G?{...G,phase:"done",error:e.error??null}:e.error?{taskId:e.taskId||null,phase:"done",text:"",thinking:"",activity:"",plan:null,model:e.model??null,effort:e.effort??null,previewMeta:e.previewMeta??null,subAgents:e.previewMeta?.subAgents??null,generatingImages:e.previewMeta?.generatingImages??0,artifacts:e.artifacts??null,error:e.error,question:e.question??null,questionBlocks:e.questionBlocks??null}:G);const v=$.current,b=!!v&&gr(v),oe=!!e.incomplete&&b&&!j;if(Pe.current!=="done"){j||(ne.current=!0),W.current=oe?!1:{taskId:e.taskId||null},R({keepOlder:!0,force:!0,scrollToBottom:z.current});const G=t.agent||"",Vt=t.sessionId,Xt=ve.current;Y.current&&clearTimeout(Y.current),Y.current=setTimeout(()=>{Y.current=null,I.getSessionStreamState(G,Vt).then(Gt=>{ve.current===Xt&&Ce.current(Gt.state,"seed")}).catch(()=>{})},900)}}j||(P.current=!1)}const at=new Set;if(e.taskId&&at.add(e.taskId),Array.isArray(e.queuedTaskIds))for(const m of e.queuedTaskIds)at.add(m);D(m=>{let j=!1;const v=[];for(const b of m)if(!b.taskId||at.has(b.taskId))v.push(b);else{for(const oe of b.imageUrls)URL.revokeObjectURL(oe);j=!0}return j?v:m}),Pe.current=e.phase},[N,V,R,t.sessionId,t.agent,y,f]),Ce=r.useRef(se);Ce.current=se;const kt=r.useCallback(()=>{P.current=!0,Se.current=Date.now(),ft(e=>e+1)},[]);r.useEffect(()=>()=>{Y.current&&(clearTimeout(Y.current),Y.current=null)},[]);const _t=r.useCallback(async e=>{try{be.current.set(e,Date.now()),await I.recallSessionMessage(e),_.current===e&&N(),D(n=>{let l=!1;const u=[];for(const d of n)if(d.taskId===e){for(const k of d.imageUrls)URL.revokeObjectURL(k);l=!0}else u.push(d);return l?u:n}),Te(n=>n.filter(l=>l!==e)),Re(n=>n.filter(l=>l.taskId!==e)),ze(n=>n===e?null:n)}catch{}},[N]),Qt=r.useCallback(async e=>{const n=we.current.find(l=>l.taskId===e)||null;if(n&&n.imageUrls.length>0){for(const l of L.current)URL.revokeObjectURL(l);Z(n.prompt||null),ee(n.imageUrls),L.current=n.imageUrls,te(e),_.current=e,D(l=>l.filter(u=>u.taskId!==e))}try{await I.steerSession(e)}catch{}},[]),$t=r.useCallback(async()=>{try{await I.stopSession(t.agent||"",t.sessionId)}catch{}},[t.agent,t.sessionId]),qe=qr(t.agent||"",t.sessionId);r.useEffect(()=>{if(tt.current){tt.current=!1;let d=!1;return R({keepOlder:!0,force:!0}).finally(()=>{d||fe(!1)}),()=>{d=!0}}let e=!1;const n=pr({workdir:f,agent:t.agent||"",sessionId:t.sessionId,rich:!0,turnOffset:0,turnLimit:it},{allowStale:!0}),l=a&&!et.current,u=n?.ok?jt(n):ae.get(qe)||null;return fe(l?!1:!u),Qe(u),F(null),ge(!1),Ke(null),Te([]),Re([]),We([]),Oe.current=0,Se.current=Date.now(),be.current=new Map,l||(N(),V(),P.current=!1,ne.current=!1,W.current=!1),z.current=!0,re.current=!0,l||R({keepOlder:!1,force:!0}).finally(()=>{e||fe(!1)}),()=>{e=!0}},[R,t.agent,t.sessionId,f,qe,N,V]),r.useEffect(()=>{i&&i.turns.length>0&&Ur(qe,i)},[qe,i]),r.useEffect(()=>{w&&R({keepOlder:!0,force:!0})},[w,R]);const ve=r.useRef(`${t.agent}:${t.sessionId}`);ve.current=`${t.agent}:${t.sessionId}`,nr("stream-update",r.useCallback(e=>{e.key===ve.current&&se(e.snapshot??null)},[se])),r.useEffect(()=>{let e=!0;return I.getSessionStreamState(t.agent||"",t.sessionId).then(n=>{e&&Ce.current(n.state,"seed")}).catch(()=>{}),()=>{e=!1}},[t.agent,t.sessionId,Mt]),sr(r.useCallback(()=>{I.getSessionStreamState(t.agent||"",t.sessionId).then(e=>{se(e.state,"seed")}).catch(()=>{}),R({keepOlder:!0,force:!0})},[se,t.agent,t.sessionId,R]));const St=B||!!U||H.length>0||!!h||je.length>0;r.useEffect(()=>{if(!w||!St)return;const e=setInterval(()=>{typeof document<"u"&&document.visibilityState!=="visible"||I.getSessionStreamState(t.agent||"",t.sessionId).then(n=>Ce.current(n.state,"seed")).catch(()=>{})},Cr);return()=>clearInterval(e)},[w,St,t.agent,t.sessionId]),r.useEffect(()=>{!P.current&&de!=="running"&&!B&&!c&&!U&&H.length===0&&(N(),V())},[de,B,c,U,H.length,N,V]),r.useLayoutEffect(()=>{const e=Ae.current,n=K.current;!e||!n||(Ae.current=null,n.scrollTop=e.scrollTop+(n.scrollHeight-e.scrollHeight))},[i?.turns.length]),r.useLayoutEffect(()=>{if(!re.current)return;const e=K.current;e&&(re.current=!1,e.scrollTop=e.scrollHeight,requestAnimationFrame(()=>{z.current&&(e.scrollTop=e.scrollHeight)}))},[i,c]),r.useLayoutEffect(()=>{if(!h)return;const e=K.current;e&&(e.scrollTop=e.scrollHeight)},[h]),r.useEffect(()=>{if(!i?.hasOlder||$e||me)return;const e=K.current;e&&e.scrollHeight<=e.clientHeight+Lt&&Me()},[i?.hasOlder,i?.turns.length,Me,$e,me]);const Kt=r.useCallback(()=>{const e=K.current;if(!e)return;const n=e.scrollHeight-e.scrollTop-e.clientHeight;z.current=n<=Nr,e.scrollTop<=Lt&&Me()},[Me]),Ie=c?.model||t.model||J||null,rt=lr(t.agent||"",c?.effort||t.thinkingEffort||T||null,t.workflowEnabled??O?.workflowEnabled)||null,bt=ce&&(!Ie||Ie===J)?ce:Ie?or(Ie):null,nt=ar(t,{streaming:B,hasLiveStream:!!c,streamPhase:U,queuedTaskCount:H.length}),A=i?.turns||[],Ue=r.useMemo(()=>{if(!M.length||!A.length)return!1;const e=A[A.length-1];return!e.user||!De(e.user.text,h)?!1:e.user.blocks.filter(l=>l.type==="image").length<M.length},[A,h,M.length]),st=r.useMemo(()=>M.length?M.map(e=>({type:"image",content:e})):!h||!c?.questionBlocks?.length?[]:De(h,c.question)?c.questionBlocks:[],[M,h,c]),vt=c?.question||null,He=hr(h,vt),lt=wt(vt,h),It=A.length>0?A[A.length-1]?.user?.text:null,yt=!!He&&A.length>0&&Fe(It,He),Tt=r.useMemo(()=>{let e=A;if(Ue){const le=e[e.length-1];e=[...e.slice(0,-1),{...le,user:null}]}if(!c||!e.length)return e;const n=e[e.length-1],l=He;if(!n.assistant)return!!n.user&&!!l&&!De(n.user.text,l)&&(Fe(n.user.text,l)||wt(l,n.user.text))?[...e.slice(0,-1),{...n,user:{...n.user,text:l}}]:e;const u=(c.text||"").trim(),d=n.assistant.text?.trim()||"";if(!(l!=null?Fe(n.user?.text,l):!!d&&!!u&&(u.startsWith(d)||d.startsWith(u))))return e;const X=n.user&&l&&!De(n.user.text,l)?{...n.user,text:l}:n.user;return[...e.slice(0,-1),{...n,user:X,assistant:null}]},[A,c,He,Ue]),zt=(!!h||st.length>0)&&!lt&&(Ue||!yt)&&!c,Wt=!!c&&ct(c)&&c.phase==="streaming",Yt=xr({sessionRunning:de==="running",streaming:B,streamPhase:U,queuedTaskCount:H.length,pendingQueuedCount:je.length,liveTurnStreaming:Wt,pendingBubbleDots:zt});return s.jsxs("div",{className:"flex flex-col h-full overflow-hidden",children:[s.jsx("div",{ref:K,onScroll:Kt,className:"flex-1 overflow-y-auto overscroll-contain",children:$e&&!h&&!M.length&&!c?s.jsx("div",{className:"flex items-center justify-center py-20",children:s.jsx(Be,{className:"h-5 w-5 text-fg-4"})}):Tt.length===0&&!h&&!M.length&&!c&&!nt?s.jsx("div",{className:"py-20 text-center text-[13px] text-fg-5",children:p("hub.noMessages")}):s.jsxs("div",{className:"max-w-[900px] mx-auto px-6 py-6 space-y-0",children:[(i?.hasOlder||me)&&s.jsxs("div",{className:"mb-4 flex items-center justify-center gap-2 text-[11px] text-fg-5",children:[me?s.jsx(Be,{className:"h-3 w-3 text-fg-5"}):s.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-fg-5/35"}),s.jsx("span",{children:p(me?"hub.loadingOlderTurns":"hub.loadOlderTurnsHint")})]}),t.migratedFrom?.kind==="fork"&&t.migratedFrom.sessionId&&s.jsxs("button",{type:"button",onClick:()=>y?.({agent:t.migratedFrom.agent||t.agent||"",sessionId:t.migratedFrom.sessionId,workdir:f}),className:"mb-4 inline-flex items-center gap-1.5 rounded-md border border-edge bg-panel-alt px-2.5 py-1 text-[11px] text-fg-5 transition hover:border-edge-h hover:text-fg-2",title:`#${t.migratedFrom.sessionId.slice(0,8)}`,children:[s.jsxs("svg",{width:"10",height:"10",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[s.jsx("circle",{cx:"6",cy:"6",r:"2"}),s.jsx("circle",{cx:"18",cy:"6",r:"2"}),s.jsx("circle",{cx:"12",cy:"20",r:"2"}),s.jsx("path",{d:"M6 8v3a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3V8"}),s.jsx("path",{d:"M12 14v4"})]}),s.jsx("span",{children:p("hub.forkBadge")}),s.jsxs("span",{className:"font-mono",children:["#",t.migratedFrom.sessionId.slice(0,8)]}),typeof t.migratedFrom.forkedAtTurn=="number"&&s.jsxs("span",{className:"text-fg-5/70",children:["· ",p("hub.forkBadgeAt").replace("{turn}",String(t.migratedFrom.forkedAtTurn+1))]})]}),Tt.map((e,n)=>{const l=(i?.startTurn||0)+n;return s.jsx(kr,{turn:e,turnIndex:l,agent:t.agent||"",meta:ie,model:bt,effort:rt,providerName:ye,t:p,workdir:f,onResend:u=>{re.current=!0,xt(u);const d=ht.current;I.sendSessionMessage(f,t.agent||"",t.sessionId,u,{model:d.model||Ie||void 0,effort:d.effort||rt||void 0}).then(k=>{k.ok&&kt()}).catch(()=>{N()})},onEdit:u=>gt(u),onFork:Ht?u=>{Ye(""),xe({atTurn:u})}:void 0},`${i?.startTurn||0}:${n}`)}),nt&&s.jsx("div",{className:"mb-5 animate-in",children:s.jsx(Sr,{detail:nt,t:p})}),(h||st.length>0)&&!lt&&(Ue||!yt)&&s.jsxs("div",{className:"session-turn",children:[s.jsx(Nt,{text:h||"",blocks:st,t:p}),!c&&s.jsx("div",{className:"mt-3 mb-5 animate-in",children:s.jsx(At,{className:"text-fg-5"})})]}),c&&ct(c)&&c.question&&(!h||lt)&&!(A.length>0&&Fe(It,c.question))&&s.jsx("div",{className:"session-turn",children:s.jsx(Nt,{text:c.question,blocks:c.questionBlocks||void 0,t:p})}),c&&ct(c)&&s.jsxs("div",{className:"mb-6",children:[s.jsx(br,{agent:t.agent||"",meta:ie,model:bt,effort:rt,providerName:ye,previewMeta:c.previewMeta,hideContextUsage:!0}),s.jsx(vr,{stream:c,t:p,workdir:f})]}),Yt&&s.jsx("div",{className:"mt-3 mb-5 animate-in",children:s.jsx(At,{className:"text-fg-5"})}),s.jsx("div",{className:"h-4"})]})}),s.jsx(Ir,{session:t,workdir:f,onStreamQueued:kt,onSendStart:xt,onSendTaskAssigned:Ft,onSessionChange:y,t:p,streamPhase:U,streamTaskId:Pt,queuedTaskIds:H,queuedTasks:Ct,pendingQueuedSends:je,onRecall:_t,onSteer:Qt,onStopAll:$t,editDraft:Ut,onEditDraftConsumed:()=>gt(null),onSelectionChange:Dt}),Ne&&s.jsxs(Ot,{open:!0,onClose:()=>{Q||xe(null)},children:[s.jsx(Et,{title:p("hub.forkPromptTitle"),description:p("hub.forkPromptHint"),onClose:()=>{Q||xe(null)}}),s.jsx("textarea",{autoFocus:!0,value:ke,disabled:Q,onChange:e=>Ye(e.target.value),onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&ke.trim()&&!Q&&(e.preventDefault(),Xe.current?.())},placeholder:p("hub.forkPromptPlaceholder"),className:"w-full min-h-[120px] resize-y rounded-md border border-edge bg-panel-alt px-3 py-2 text-[13px] leading-relaxed text-fg outline-none focus:border-edge-h"}),s.jsxs("div",{className:"mt-4 flex items-center justify-end gap-2",children:[s.jsx(_e,{variant:"ghost",disabled:Q,onClick:()=>xe(null),children:p("modal.cancel")}),s.jsx(_e,{variant:"primary",disabled:Q||!ke.trim(),onClick:()=>{Xe.current?.()},children:p(Q?"hub.forkSubmitting":"hub.forkSubmit")})]})]}),w&&pe.length>0&&s.jsx(yr,{snapshot:pe[pe.length-1]},pe[pe.length-1].promptId)]})});export{Yr as SessionPanel};