@agimon-ai/doompi-workflow 0.0.1-alpha.13 → 0.0.1-alpha.14

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 (42) hide show
  1. package/README.md +6 -0
  2. package/dist/adapters/pi/workflow/piExtension.cjs +8 -8
  3. package/dist/adapters/pi/workflow/piExtension.cjs.map +1 -1
  4. package/dist/adapters/pi/workflow/piExtension.mjs +8 -8
  5. package/dist/adapters/pi/workflow/piExtension.mjs.map +1 -1
  6. package/dist/adapters/pi/workflow/piToolBridge.cjs +1 -1
  7. package/dist/adapters/pi/workflow/piToolBridge.cjs.map +1 -1
  8. package/dist/adapters/pi/workflow/piToolBridge.mjs +1 -1
  9. package/dist/adapters/pi/workflow/piToolBridge.mjs.map +1 -1
  10. package/dist/adapters/pi/workflow/piTools.cjs +3 -2
  11. package/dist/adapters/pi/workflow/piTools.cjs.map +1 -1
  12. package/dist/adapters/pi/workflow/piTools.mjs +3 -2
  13. package/dist/adapters/pi/workflow/piTools.mjs.map +1 -1
  14. package/dist/services/workflow/workflowNarration.cjs +2 -0
  15. package/dist/services/workflow/workflowNarration.cjs.map +1 -0
  16. package/dist/services/workflow/workflowNarration.mjs +2 -0
  17. package/dist/services/workflow/workflowNarration.mjs.map +1 -0
  18. package/dist/tui/workflow/workflowInspector.cjs +1 -1
  19. package/dist/tui/workflow/workflowInspector.cjs.map +1 -1
  20. package/dist/tui/workflow/workflowInspector.mjs +1 -1
  21. package/dist/tui/workflow/workflowInspector.mjs.map +1 -1
  22. package/dist/tui/workflow/workflowProgressOverlay.cjs +2 -0
  23. package/dist/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
  24. package/dist/tui/workflow/workflowProgressOverlay.mjs +2 -0
  25. package/dist/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
  26. package/dist/tui/workflow/workflowStatusRow.cjs +1 -1
  27. package/dist/tui/workflow/workflowStatusRow.cjs.map +1 -1
  28. package/dist/tui/workflow/workflowStatusRow.mjs +1 -1
  29. package/dist/tui/workflow/workflowStatusRow.mjs.map +1 -1
  30. package/dist/tui/workflow/workflowStepMessage.cjs +1 -1
  31. package/dist/tui/workflow/workflowStepMessage.cjs.map +1 -1
  32. package/dist/tui/workflow/workflowStepMessage.mjs +1 -1
  33. package/dist/tui/workflow/workflowStepMessage.mjs.map +1 -1
  34. package/dist/tui/workflow/workflowToolRender.cjs +7 -0
  35. package/dist/tui/workflow/workflowToolRender.cjs.map +1 -0
  36. package/dist/tui/workflow/workflowToolRender.mjs +7 -0
  37. package/dist/tui/workflow/workflowToolRender.mjs.map +1 -0
  38. package/package.json +6 -6
  39. package/dist/tui/workflow/workflowFooterStatus.cjs +0 -2
  40. package/dist/tui/workflow/workflowFooterStatus.cjs.map +0 -1
  41. package/dist/tui/workflow/workflowFooterStatus.mjs +0 -2
  42. package/dist/tui/workflow/workflowFooterStatus.mjs.map +0 -1
@@ -1,11 +1,11 @@
1
- import{LEADER_DISABLE_ACTION as e,LEADER_ENABLE_ACTION as t,LEADER_LAUNCH_ACTION as n,LEADER_MANAGE_ACTION as r,LEADER_RECOVER_ACTION as i,PACKAGE_SOURCE as a}from"../../../types/index.mjs";import{registerRunProvider as o}from"../../../services/workflow/backgroundWork.mjs";import{finishedRunSummary as s,isSessionRun as c,launchNotice as l,runsForSession as u,toolResultText as d,withOptions as f}from"./piToolBridge.mjs";import{WORKFLOW_PI_TOOL_NAMES as p,createWorkflowLaunchExecutor as ee,registerWorkflowPiTools as te}from"./piTools.mjs";import{WORKFLOW_PULSE_FRAMES as ne,workflowFooterStatus as re}from"../../../tui/workflow/workflowFooterStatus.mjs";import{stepReportLines as ie}from"../../../tui/workflow/workflowStatusRow.mjs";import{WorkflowInspectorComponent as ae}from"../../../tui/workflow/workflowInspector.mjs";import{openWorkflowLaunchWizard as oe}from"./workflowLauncher.mjs";import{TerminalInputBatcher as se}from"../../../tui/workflow/workflowOverlay.mjs";import{openWorkflowPickerOverlay as ce}from"../../../tui/workflow/workflowPicker.mjs";import{openWorkflowChoice as le,openWorkflowInput as ue}from"../../../tui/workflow/workflowPrompt.mjs";import{WorkflowRunPanelComponent as de,runPanelUiOptions as fe}from"../../../tui/workflow/workflowRunPanel.mjs";import{formatWorkflowStepMessage as pe,isWorkflowStepMessageDetails as me,renderWorkflowStepMessage as he,workflowStepMessageDetails as ge}from"../../../tui/workflow/workflowStepMessage.mjs";import{createRequire as m}from"node:module";import{SUBAGENT_ROOT_SESSION_ENV as h,resolveRootSessionId as g}from"@agimon-ai/doompi-extension-contracts/child-process";import{createProtocolRuntime as _e}from"@agimon-ai/doompi-extension-contracts/protocol";import{WorkflowModeChanged as ve}from"@agimon-ai/doompi-extension-contracts/workflow";import{spawn as _}from"node:child_process";import{existsSync as ye}from"node:fs";import{readFile as be}from"node:fs/promises";import{basename as xe,dirname as Se,extname as Ce,resolve as v}from"node:path";import{fileURLToPath as we}from"node:url";import{registerDoomFooterContribution as Te}from"@agimon-ai/doompi-extension-contracts/footer";import{registerDoomLeaderActionHandlers as Ee}from"@agimon-ai/doompi-extension-contracts/leader";import{registerDoomModeContribution as De}from"@agimon-ai/doompi-extension-contracts/mode";import{registerDoomSkillSource as Oe}from"@agimon-ai/doompi-extension-contracts/skills";import{createDoomTelemetry as ke}from"@agimon-ai/doompi-telemetry";import{createEmbeddedWorkflowFeature as Ae,readWorkflowProgress as je,summarizeWorkflowProgress as Me}from"@agimon-ai/workflow-mcp";import{truncateToWidth as y,visibleWidth as b}from"@earendil-works/pi-tui";const Ne={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},Pe=`agiflow-workflow`,Fe=`workflow-step`,Ie=`workflow-mcp-follow`,Le=`SPC › w / workflows › w / launch`,Re=`paused`,x=`ctrl+alt+w`,S=`ctrl+alt+q`,ze={alt:`⌥`,cmd:`⌘`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`};function C(e,t=process.platform){let n=e.split(`+`);return t===`darwin`?n.map(e=>ze[e]??e.toUpperCase()).join(``):n.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`+`)}const w=1e4,T=`running`,E=`completed`,D=`failed`,O=`error`,Be=`error`,Ve=`doom_workflow.launch_requested`,He=`../../../../skills`,k={running:`▸`,repair:`⚠`,completed:`✔`,failed:`✖`,separator:`·`};function Ue(e,t=process.platform){let n=C(S,t);return e?[`Esc Esc closes this view`,`${C(x,t)} switches typing`,`${n} closes`].join(` ${k.separator} `):`view only ${k.separator} ${n} closes this view`}function We(e,t=process.platform){let n=[C(S,t),`close`];return e?[[`Esc Esc`,`close`],[C(x,t),`switch typing`],n]:[n]}function Ge(e,t,n,r){let i=`${k.running} ${e.runKey}`,a=[e.workspace,Je(e),e.runner].filter(Boolean).join(` ${k.separator} `),o=`${C(x)} to background`,s=n.fg(`accent`,y(i,r));return b(i)+3+b(a)<=r&&(s+=` `+n.fg(`muted`,a),b(i)+3+b(a)+3+b(o)<=r&&(s+=` `+n.fg(`dim`,o))),[s,...t.map(e=>y(e,r))]}function Ke(e,t,n){let r=[];for(let i of e){let e=i.status===E?k.completed:i.status===D?k.failed:k.running,a=i.status===E?`success`:i.status===D?Be:`accent`,o=i.total?` ${k.separator} ${(i.index??0)+1}/${i.total}`:``;if(r.push(y(t.fg(a,`${e} ${i.name}`)+t.fg(`dim`,o),n)),i.status===T)for(let e of i.steps){let i=e.status===E?k.completed:e.status===D?k.failed:k.running,a=e.status===T?`muted`:e.status===D?Be:`dim`;r.push(y(t.fg(a,` ${i} ${e.name}`),n))}}return r}function qe(e,t){let n=Date.parse(e.startedAt);return Number.isFinite(n)?t.filter(e=>{let t=Date.parse(e.at);return!Number.isFinite(t)||t>=n}):t}function Je(e){return e.stale?`stale`:e.stage===O?D:e.stage}const Ye=[`CMUX_SURFACE_ID`,h,`CMUX_WORKSPACE_ID`,`WORKFLOW_LAUNCHER_TYPE`,`WORKFLOW_TMUX_SESSION_NAME`,`WORKFLOW_STATUS_FILE`],A=`tmux`,j=`cmux`,Xe=`native`,Ze=`No durable launcher identity is recorded for this run.`;function Qe(e){return xe(e,Ce(e))}function $e(){let e={...process.env};for(let t of Ye)delete e[t];return e}function et(){let e=process.env.WORKFLOW_LAUNCHER;return e===A||e===j?e:void 0}function tt(e){return e.map(e=>`'${e.replaceAll(`'`,`'\\''`)}'`).join(` `)}function nt(){try{return m(import.meta.url).resolve(`@agimon-ai/workflow-mcp/cli`)}catch{return}}const rt=async(e,t,n)=>{await new Promise((r,i)=>{let a=_(e,[...t],{...n,detached:!0,stdio:`ignore`});a.once(`error`,i),a.once(`spawn`,()=>{a.off(`error`,i),a.unref(),r()})})};function it(e,t){if(Array.isArray(e)){for(let n of e)it(n,t);return}if(!(!e||typeof e!=`object`))for(let[n,r]of Object.entries(e))(n===`interactiveRun`||n===`run`)&&r&&typeof r==`object`&&!Array.isArray(r)?t.push(Object.keys(r)):it(r,t)}function at(e){let t=[];if(it(e,t),t.length!==0)return t[0].filter(e=>t.every(t=>t.includes(e))).sort()}async function M(e,t){let n=[],r=1,i=!0;for(;i;){let a=await e.listRunsPage({page:r,pageSize:100,workspace:t});n.push(...a.items),i=a.hasNextPage,r+=1}return n}function ot(e){return[e.runKey,e.workspace,Je(e),e.runner].filter(Boolean).join(` ${k.separator} `)}function N(m={}){return h=>{let _=m.featureFactory?.()??Ae(),{parser:Ce,recoverService:y,registry:b,runService:ze,runTool:D}=_,Be=m.spawnDetached??rt,Ye=m.monitorIntervalMs??3e4,it=m.followIntervalMs??2e3,N,st=!1,P,ct=!1,F,lt=new Map,I=new Map,ut=new Set,dt=0,L,ft,pt,mt=0,R=!1,ht=!1,gt=De(h,{source:a,id:`workflow`,order:20}),_t={runs:[],failed:0},vt,yt=0,z,bt=Te(h,{source:a,id:`workflow-runs`,order:50}),xt,B,V,St=!1,Ct=new Map,H,wt=[],U=new Map,Tt=new Map,W=new Map,Et=[],Dt=new Map,Ot,G,K=e=>(G??=ke({serviceName:`doom-workflow`,packageName:`@agimon-ai/doompi-workflow`,cwd:e?.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),G),kt=e=>{e&&(Ot=g(e))},At=()=>{let e=Ot??xt?.sessionManager.getSessionId();return e?g(e):void 0},jt=(e,t)=>e.sessionManager.getSessionId().trim()===t,Mt=_.createRecoverTool({ownerSessionId:At}),Nt=_.createControlTool(),q=new Set,Pt=e=>{let t=Date.now();return q.add(e),K().recordEvent(`doom_workflow.run_started`,{outcome:`started`}),e.then(()=>(q.delete(e),K().recordEvent(`doom_workflow.run_finished`,{duration_ms:Date.now()-t,outcome:`completed`})),n=>(q.delete(e),K().recordError(`doom_workflow.run_failed`,n,{duration_ms:Date.now()-t}))),e},Ft=(e,t)=>{let n=at(Ce.parseWorkflowFile(e));if(!(!n||n.includes(t)))return`Runner ${JSON.stringify(t)} is not available across this workflow. Compatible runners: ${n.join(`, `)||`none`}.`},It=ee({activeRunCount:async()=>mt,observeSession:kt,rejectRunner:Ft,runTool:D,trackPendingRun:Pt,...m.launchAckPollMs===void 0?{}:{launchAckPollMs:m.launchAckPollMs},...m.launchAckTimeoutMs===void 0?{}:{launchAckTimeoutMs:m.launchAckTimeoutMs},findLaunchedRun:async({sessionId:e,since:t,workflowPath:n})=>{let r=Qe(n),i=n=>{if(!c(n,e)||n.stale||n.workflowId&&n.workflowId!==r)return!1;let i=Date.parse(n.startedAt);return Number.isFinite(i)&&i>=t-2e3};return H?(await H.refresh(),Et.find(i)):(await M(b)).find(i)},onLateFailure:(e,t)=>{let n=e instanceof Error?e.message:String(e);t.ui.notify(l(`Workflow launch failed after it was reported started.\n${n}`),`warning`)},onLaunch:async e=>(await K(e).recordEvent(Ve,{outcome:`requested`}),X(e,!1))}),Lt=(e,t)=>{if(!e.isIdle())return;let n=Y(t),r=Dt.get(n);r||(r=new Set,Dt.set(n,r));for(let e of ie(t.runKey,W.get(n)??[])){if(r.has(e.key))continue;r.add(e.key);let n=ge(t.displayName,e);h.sendMessage({customType:Fe,content:pe(n),details:n,display:!0},{triggerTurn:!1})}},Rt=e=>e.filter(e=>e.stage===T&&!e.stale).map(e=>{let t=Y(e),n=U.get(t),r=n?[...n.values()].includes(T):!1,i=e.executionState===Re?`paused`:r?`running`:`starting`,a=Tt.get(t);return{key:t,state:i,...a===void 0?{}:{progress:a}}}),zt=(e,t,n,r)=>{let i=U.get(e)??new Map;(r||!i.has(t))&&i.set(t,n),U.set(e,i)},Bt=(e,t)=>{t.phase===`job`&&t.status===T&&(t.index===void 0||!t.total||Tt.set(e,Math.min(1,Math.max(0,t.index)/t.total)))},J,Vt=e=>{J||(J=setTimeout(()=>{J=void 0,X(e,!0)},120),J.unref?.())},Ht=e=>{W.delete(e),U.delete(e),Tt.delete(e),Dt.delete(e)},Ut=async e=>{let t=g(e.sessionManager.getSessionId()),n=_.createRunControl({recordFilter:e=>c(e,t)});H=n,wt.push(n.on(`step`,t=>{let n=Y(t.record),r=W.get(n)??[];r.push({at:t.at,job:t.job,status:t.status,step:t.step,type:`step`}),W.set(n,r),zt(n,t.job,T,!1),Lt(e,t.record),Vt(e)}),n.on(`job`,t=>{let n=Y(t.record);zt(n,t.job,t.status,!0),Bt(n,t),Vt(e)}),n.on(`runStarted`,t=>{Et.unshift(t.record),Et.length>32&&(Et.length=32),Vt(e)}),n.on(`runUpdated`,()=>void X(e,!0)),n.on(`runFinished`,()=>void X(e,!0))),await n.start()},Wt=()=>{J&&clearTimeout(J),J=void 0;for(let e of wt.splice(0))e();H?.dispose(),H=void 0,W.clear(),U.clear(),Tt.clear(),Dt.clear()},Gt=()=>{B?.teardown(),B?.handle?.hide(),B=void 0},Y=e=>e.runId??`${e.workspace}/${e.runKey}`,Kt=(e,t=!1)=>{let n=re(_t,yt);!t&&n?.fingerprint===vt||(vt=n?.fingerprint,bt.update(n?.footer),e.ui.setStatus(Pe,n?.text),Jt(e,n?.pulsing??!1))},qt=()=>{R!==ht&&(ht=R,gt.update(R?{label:`WORKFLOW`,color:`accent`}:void 0))},Jt=(e,t)=>{if(!t){z&&clearInterval(z),z=void 0;return}z||(z=setInterval(()=>{yt=(yt+1)%ne.length,Kt(e)},250),z.unref?.())},X=async(e,t)=>{let n=K(e);if(st){n.recordEvent(`doom_workflow.monitor_skipped`,{outcome:`already_running`});return}let r=Date.now();st=!0;try{let i=await M(b),a=g(e.sessionManager.getSessionId()),o=jt(e,a),l=i.filter(e=>c(e,a)),d=l.filter(e=>e.stage===T&&!e.stale),f=l.filter(e=>e.stage===O);n.recordEvent(`doom_workflow.monitor_counts`,{"workflow.run_count":l.length,"workflow.running_count":d.length,"workflow.failed_count":f.length,mode:R?`on`:`off`});let p=u(i,a).filter(e=>!e.stale);mt=p.length,L?.update(p.map(e=>({id:e.runId??e.runKey,sessionId:a}))),_t={runs:Rt(l),failed:f.length},Kt(e,!0),Qt(new Set(l.map(e=>Y(e))));let ee=new Set(l.filter(e=>e.stage===T&&!e.stale).map(e=>Y(e)));for(let e of[...W.keys(),...U.keys()])!ee.has(e)&&!I.has(e)&&Ht(e);let te=new Map(l.map(e=>[Y(e),e.stage]));if(t&&o)for(let e of l){if(e.stage===T)continue;let t=Y(e),n=lt.get(t),r=Date.parse(e.startedAt),i=n===void 0&&(dt===0||!Number.isFinite(r)||r>=dt);!ut.has(t)&&(n===T||i)&&I.set(t,e)}if(t)for(let t of l){if(lt.get(Y(t))!==T||t.stage===T)continue;let r=t.stage===E,i=t.failedJob?` at ${t.failedJob}`:``;e.ui.notify(r?`${k.completed} ${t.runKey} completed`:`${k.failed} ${t.runKey} ${Je(t)}${i}`,r?`info`:`warning`),n.recordEvent(`doom_workflow.run_transition`,{outcome:r?`completed`:Je(t)})}if(o&&I.size>0)try{let e=[...I.values()],t=await Promise.all(e.map(async e=>{if(e.stage===E)return s(e,[]);let t=Y(e),n=W.get(t)??qe(e,await je(b.runDirectoryFor(e)));return s(e,Me(n))}));h.sendMessage({customType:`workflow-run-finished`,content:t.join(`
1
+ import{registerRunProvider as e}from"../../../services/workflow/backgroundWork.mjs";import{narrateWorkflowTransition as t}from"../../../services/workflow/workflowNarration.mjs";import{WorkflowInspectorComponent as n}from"../../../tui/workflow/workflowInspector.mjs";import{TerminalInputBatcher as r}from"../../../tui/workflow/workflowOverlay.mjs";import{openWorkflowPickerOverlay as i}from"../../../tui/workflow/workflowPicker.mjs";import{WorkflowProgressOverlay as a,workflowProgressRow as o}from"../../../tui/workflow/workflowProgressOverlay.mjs";import{openWorkflowChoice as s,openWorkflowInput as c}from"../../../tui/workflow/workflowPrompt.mjs";import{WorkflowRunPanelComponent as l,runPanelUiOptions as u}from"../../../tui/workflow/workflowRunPanel.mjs";import{isWorkflowStepMessageDetails as ee,renderWorkflowStepMessage as d}from"../../../tui/workflow/workflowStepMessage.mjs";import{LEADER_DISABLE_ACTION as f,LEADER_ENABLE_ACTION as te,LEADER_LAUNCH_ACTION as ne,LEADER_MANAGE_ACTION as re,LEADER_RECOVER_ACTION as ie,PACKAGE_SOURCE as ae}from"../../../types/index.mjs";import{finishedRunSummary as oe,isSessionRun as p,launchNotice as se,runsForSession as ce,toolResultText as le,withOptions as ue}from"./piToolBridge.mjs";import{WORKFLOW_PI_TOOL_NAMES as de,createWorkflowLaunchExecutor as fe,registerWorkflowPiTools as pe}from"./piTools.mjs";import{openWorkflowLaunchWizard as me}from"./workflowLauncher.mjs";import{createRequire as m}from"node:module";import{SUBAGENT_ROOT_SESSION_ENV as h,resolveRootSessionId as g}from"@agimon-ai/doompi-extension-contracts/child-process";import{createProtocolRuntime as he}from"@agimon-ai/doompi-extension-contracts/protocol";import{WorkflowModeChanged as ge}from"@agimon-ai/doompi-extension-contracts/workflow";import{spawn as _}from"node:child_process";import{existsSync as _e}from"node:fs";import{readFile as ve}from"node:fs/promises";import{basename as ye,dirname as be,extname as xe,resolve as v}from"node:path";import{fileURLToPath as Se}from"node:url";import{registerDoomLeaderActionHandlers as Ce}from"@agimon-ai/doompi-extension-contracts/leader";import{registerMinorModeOwner as we}from"@agimon-ai/doompi-extension-contracts/mode";import{createNarrationRequester as Te}from"@agimon-ai/doompi-extension-contracts/narration";import{registerDoomSkillSource as Ee}from"@agimon-ai/doompi-extension-contracts/skills";import{createDoomTelemetry as De}from"@agimon-ai/doompi-telemetry";import{createEmbeddedWorkflowFeature as Oe,readWorkflowProgress as ke,summarizeWorkflowProgress as Ae}from"@agimon-ai/workflow-mcp";import{truncateToWidth as y,visibleWidth as b}from"@earendil-works/pi-tui";const je={overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}},Me={narrate:()=>void 0},Ne=`workflow-mcp-follow`,Pe=`activate`,Fe=`deactivate`;function Ie(e,t){return{activation:e?`active`:`inactive`,condition:`ready`,...e?{detail:t>0?`${t} active runs`:`tools enabled`,color:`accent`}:{},actions:[...e?[{id:Pe,enabled:!1,disabledReason:`Workflow mode is active.`}]:[{id:Pe,enabled:!0}],...e?[{id:Fe,enabled:!0}]:[{id:Fe,enabled:!1,disabledReason:`Workflow mode is inactive.`}]]}}const Le=`SPC › w / workflows › w / launch`,x=`ctrl+alt+w`,S=`ctrl+alt+q`,Re={alt:`⌥`,cmd:`⌘`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`};function C(e,t=process.platform){let n=e.split(`+`);return t===`darwin`?n.map(e=>Re[e]??e.toUpperCase()).join(``):n.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(`+`)}const w=1e4,T=`running`,E=`completed`,D=`failed`,O=`error`,ze=`error`,Be=`doom_workflow.launch_requested`,Ve=`../../../../skills`,k={running:`▸`,repair:`⚠`,completed:`✔`,failed:`✖`,separator:`·`};function He(e,t=process.platform){let n=C(S,t);return e?[`Esc Esc closes this view`,`${C(x,t)} switches typing`,`${n} closes`].join(` ${k.separator} `):`view only ${k.separator} ${n} closes this view`}function Ue(e,t=process.platform){let n=[C(S,t),`close`];return e?[[`Esc Esc`,`close`],[C(x,t),`switch typing`],n]:[n]}function We(e,t,n,r){let i=`${k.running} ${e.runKey}`,a=[e.workspace,A(e),e.runner].filter(Boolean).join(` ${k.separator} `),o=`${C(x)} to background`,s=n.fg(`accent`,y(i,r));return b(i)+3+b(a)<=r&&(s+=` `+n.fg(`muted`,a),b(i)+3+b(a)+3+b(o)<=r&&(s+=` `+n.fg(`dim`,o))),[s,...t.map(e=>y(e,r))]}function Ge(e,t,n){let r=[];for(let i of e){let e=i.status===E?k.completed:i.status===D?k.failed:k.running,a=i.status===E?`success`:i.status===D?ze:`accent`,o=i.total?` ${k.separator} ${(i.index??0)+1}/${i.total}`:``;if(r.push(y(t.fg(a,`${e} ${i.name}`)+t.fg(`dim`,o),n)),i.status===T)for(let e of i.steps){let i=e.status===E?k.completed:e.status===D?k.failed:k.running,a=e.status===T?`muted`:e.status===D?ze:`dim`;r.push(y(t.fg(a,` ${i} ${e.name}`),n))}}return r}function Ke(e,t){let n=Date.parse(e.startedAt);return Number.isFinite(n)?t.filter(e=>{let t=Date.parse(e.at);return!Number.isFinite(t)||t>=n}):t}function A(e){return e.stale?`stale`:e.stage===O?D:e.stage}const qe=`CMUX_WORKSPACE_ID`,Je=`TMUX_PANE`,j=`WORKFLOW_TMUX_SESSION_NAME`,Ye=[`CMUX_SURFACE_ID`,h,qe,Je,`WORKFLOW_LAUNCHER_TYPE`,j,`WORKFLOW_STATUS_FILE`],M=`tmux`,N=`cmux`,Xe=`native`,Ze=`This run points at Pi's own terminal, so its launcher screen is hidden. Use workflow status instead.`;function Qe(e){let t=e.launcher;if(t?.type===N){let e=process.env[qe];return!!e&&t.workspaceId===e}if(t?.type!==M)return!1;let n=process.env[Je],r=process.env[j];return!!n&&t.paneId===n||!!r&&t.sessionName===r}const $e=`No durable launcher identity is recorded for this run.`;function et(e){return ye(e,xe(e))}function tt(){let e={...process.env};for(let t of Ye)delete e[t];return e}function nt(){let e=process.env.WORKFLOW_LAUNCHER;return e===M||e===N?e:void 0}function rt(e){return e.map(e=>`'${e.replaceAll(`'`,`'\\''`)}'`).join(` `)}function it(){try{return m(import.meta.url).resolve(`@agimon-ai/workflow-mcp/cli`)}catch{return}}const at=async(e,t,n)=>{await new Promise((r,i)=>{let a=_(e,[...t],{...n,detached:!0,stdio:`ignore`});a.once(`error`,i),a.once(`spawn`,()=>{a.off(`error`,i),a.unref(),r()})})};function P(e,t){if(Array.isArray(e)){for(let n of e)P(n,t);return}if(!(!e||typeof e!=`object`))for(let[n,r]of Object.entries(e))(n===`interactiveRun`||n===`run`)&&r&&typeof r==`object`&&!Array.isArray(r)?t.push(Object.keys(r)):P(r,t)}function ot(e){let t=[];if(P(e,t),t.length!==0)return t[0].filter(e=>t.every(t=>t.includes(e))).sort()}async function F(e,t){let n=[],r=1,i=!0;for(;i;){let a=await e.listRunsPage({page:r,pageSize:100,workspace:t});n.push(...a.items),i=a.hasNextPage,r+=1}return n}function st(e){return[e.runKey,e.workspace,A(e),e.runner].filter(Boolean).join(` ${k.separator} `)}function ct(m={}){return h=>{let _=m.featureFactory?.()??Oe(),{parser:xe,recoverService:y,registry:b,runService:Re,runTool:D}=_,ze=m.spawnDetached??at,qe=m.monitorIntervalMs??3e4,Je=m.followIntervalMs??2e3,j,Ye=!1,P,ct=!1,I,lt=new Map,L=new Map,ut=new Set,dt=0,R,ft,pt=Me,mt=!1,ht,z=0,B=!1,gt,_t=we(h,{descriptor:{source:ae,id:`workflow`,label:`WORKFLOW`,description:`Workflow discovery, launch, inspection, control, and recovery tools.`,order:20,actions:[{id:Pe,label:`Activate`,description:`Enable workflow tools for this session.`,contexts:[`tui`,`headless`],parameters:[]},{id:Fe,label:`Deactivate`,description:`Disable workflow tools without stopping active runs.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:Ie(!1,0),async handleAction(e,t,n){if(e===Pe)return await fn(!0,n.context),{message:`Workflow mode activated.`};if(e===Fe)return await fn(!1,n.context),{message:`Workflow mode deactivated.`};throw Error(`Unknown workflow mode action: ${e}`)}}),vt,V,yt,bt=!1,xt=new Map,H,St=[],U=new Map,Ct=[],W=new Map,wt=0,Tt=new a,Et,G,K=e=>(G??=De({serviceName:`doom-workflow`,packageName:`@agimon-ai/doompi-workflow`,cwd:e?.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),G),Dt=e=>{e&&(Et=g(e))},Ot=()=>{let e=Et??vt?.sessionManager.getSessionId();return e?g(e):void 0},kt=(e,t)=>e.sessionManager.getSessionId().trim()===t,At=_.createRecoverTool({ownerSessionId:Ot}),jt=_.createControlTool(),q=new Set,Mt=e=>{let t=Date.now();return q.add(e),K().recordEvent(`doom_workflow.run_started`,{outcome:`started`}),e.then(()=>(q.delete(e),K().recordEvent(`doom_workflow.run_finished`,{duration_ms:Date.now()-t,outcome:`completed`})),n=>(q.delete(e),K().recordError(`doom_workflow.run_failed`,n,{duration_ms:Date.now()-t}))),e},Nt=(e,t)=>{let n=ot(xe.parseWorkflowFile(e));if(!(!n||n.includes(t)))return`Runner ${JSON.stringify(t)} is not available across this workflow. Compatible runners: ${n.join(`, `)||`none`}.`},Pt=fe({activeRunCount:async()=>z,observeSession:Dt,rejectRunner:Nt,runTool:D,trackPendingRun:Mt,...m.launchAckPollMs===void 0?{}:{launchAckPollMs:m.launchAckPollMs},...m.launchAckTimeoutMs===void 0?{}:{launchAckTimeoutMs:m.launchAckTimeoutMs},findLaunchedRun:async({sessionId:e,since:t,workflowPath:n})=>{let r=et(n),i=n=>{if(!p(n,e)||n.stale||n.workflowId&&n.workflowId!==r)return!1;let i=Date.parse(n.startedAt);return Number.isFinite(i)&&i>=t-2e3};return H?(await H.refresh(),Ct.find(i)):(await F(b)).find(i)},onLateFailure:(e,t)=>{let n=e instanceof Error?e.message:String(e);t.ui.notify(se(`Workflow launch failed after it was reported started.\n${n}`),`warning`)},onLaunch:async e=>(await K(e).recordEvent(Be,{outcome:`requested`}),Z(e,!1))}),Ft=()=>{let e=[...W.values()].sort((e,t)=>Date.parse(e.startedAt)-Date.parse(t.startedAt)||X(e).localeCompare(X(t)));Tt.update(e.map(e=>o(e,U.get(X(e))??[])))},J=e=>{wt+=1;let t=X(e);e.stage===T&&!e.stale?W.set(t,e):W.delete(t),Ft()},It=e=>{W.clear();for(let t of e)W.set(X(t),t);Ft()},Lt=(e,t)=>{let n=U.get(e)??[];n.push(t),U.set(e,n)},Y,Rt=e=>{Y||(Y=setTimeout(()=>{Y=void 0,Z(e,!0)},120),Y.unref?.())},zt=e=>{U.delete(e)},Bt=async e=>{let t=g(e.sessionManager.getSessionId()),n=_.createRunControl({recordFilter:e=>p(e,t)});H=n,St.push(n.on(`step`,t=>{let n=X(t.record);Lt(n,{at:t.at,job:t.job,status:t.status,step:t.step,type:`step`,...t.reason===void 0?{}:{reason:t.reason}}),J(t.record),Rt(e)}),n.on(`job`,t=>{let n=X(t.record);Lt(n,{at:t.at,job:t.job,status:t.status,type:`job`,...t.index===void 0?{}:{index:t.index},...t.reason===void 0?{}:{reason:t.reason},...t.total===void 0?{}:{total:t.total}}),J(t.record),Rt(e)}),n.on(`runStarted`,t=>{Ct.unshift(t.record),Ct.length>32&&(Ct.length=32),J(t.record),Rt(e)}),n.on(`runUpdated`,t=>{J(t.record),Z(e,!0)}),n.on(`runFinished`,t=>{J(t.record),Z(e,!0)})),await n.start()},Vt=()=>{Y&&clearTimeout(Y),Y=void 0;for(let e of St.splice(0))e();H?.dispose(),H=void 0,U.clear(),wt+=1,W.clear(),Ft()},Ht=()=>{V?.teardown(),V?.handle?.hide(),V=void 0},X=e=>e.runId??`${e.workspace}/${e.runKey}`,Ut=()=>{let e=Ie(B,z),t=JSON.stringify(e);t!==gt&&(gt=t,_t.publish(e))},Z=async(e,n)=>{let r=K(e);if(Ye){r.recordEvent(`doom_workflow.monitor_skipped`,{outcome:`already_running`});return}let i=Date.now(),a=wt;Ye=!0;try{let o=await F(b),s=g(e.sessionManager.getSessionId()),c=kt(e,s),l=o.filter(e=>p(e,s)),u=l.filter(e=>e.stage===T&&!e.stale),ee=l.filter(e=>e.stage===O);a===wt&&It(u),r.recordEvent(`doom_workflow.monitor_counts`,{"workflow.run_count":l.length,"workflow.running_count":u.length,"workflow.failed_count":ee.length,mode:B?`on`:`off`});let d=ce(o,s).filter(e=>!e.stale);z=d.length,Ut(),R?.update(d.map(e=>({id:e.runId??e.runKey,sessionId:s}))),Jt(new Set(l.map(e=>X(e))));let f=new Map(l.map(e=>[X(e),e.stage]));for(let e of l){let r=X(e),i=lt.get(r),a=Date.parse(e.startedAt),o=i===void 0&&(dt===0||!Number.isFinite(a)||a>=dt);mt&&t(pt,e,i===T||i===E||i===O?i:void 0,o),!(!n||!c||e.stage===T)&&!ut.has(r)&&(i===T||o)&&L.set(r,e)}let te=new Set([...l.filter(e=>e.stage===T&&!e.stale).map(e=>X(e)),...L.keys()]);for(let e of U.keys())te.has(e)||zt(e);if(n)for(let t of l){if(lt.get(X(t))!==T||t.stage===T)continue;let n=t.stage===E,i=t.failedJob?` at ${t.failedJob}`:``;e.ui.notify(n?`${k.completed} ${t.runKey} completed`:`${k.failed} ${t.runKey} ${A(t)}${i}`,n?`info`:`warning`),r.recordEvent(`doom_workflow.run_transition`,{outcome:n?`completed`:A(t)})}if(c&&L.size>0)try{let e=[...L.values()],t=await Promise.all(e.map(async e=>{if(e.stage===E)return oe(e,[]);let t=X(e),n=U.get(t)??Ke(e,await ke(b.runDirectoryFor(e)));return oe(e,Ae(n))}));h.sendMessage({customType:`workflow-run-finished`,content:t.join(`
2
2
 
3
- `),display:!0,details:{runIds:e.map(e=>e.runId??Y(e))}},{triggerTurn:!0,deliverAs:`steer`});for(let t of e){let e=Y(t);ut.add(e),I.delete(e),Ht(e)}}catch(e){await n.recordError(`doom_workflow.terminal_delivery_failed`,e,{"workflow.pending_terminal_count":I.size})}lt=te,n.recordEvent(`doom_workflow.monitor_finished`,{duration_ms:Date.now()-r,outcome:`completed`})}catch(t){await n.recordError(`doom_workflow.monitor_failed`,t,{duration_ms:Date.now()-r}),e.ui.setStatus(Pe,void 0),e.ui.notify(`Workflow status refresh failed: ${t instanceof Error?t.message:String(t)}`,`warning`)}finally{st=!1}},Yt=e=>{P&&clearInterval(P),P=void 0,F=void 0,e.ui.setWidget(Ie,void 0)},Xt=async e=>{let t=e.launcher;if(!t)return[Ze];if(t.type===A){let n=t.paneId??t.sessionId??t.sessionName,r=await h.exec(A,[`capture-pane`,`-p`,`-t`,n,`-S`,`-24`],{timeout:w});return r.code===0?r.stdout.trimEnd().split(`
4
- `).slice(-24):[`Unable to read ${e.runKey}: ${r.stderr.trim()||`tmux exited ${r.code}`}`]}if(t.type===Xe)return[`This run is hosted natively and has no launcher screen to read.`];let n=await h.exec(j,[`read-screen`,`--workspace`,t.workspaceId,`--scrollback`,`--lines`,`24`],{timeout:w});return n.code===0?n.stdout.trimEnd().split(`
5
- `).slice(-24):[`Unable to read ${e.runKey}: ${n.stderr.trim()||`cmux exited ${n.code}`}`]},Z=new Map,Zt=async e=>{let t=Y(e),n=Z.get(t)??{at:0,lines:[]};if(Z.set(t,n),n.inFlight||Date.now()-n.at<750)return n.lines;n.inFlight=Xt(e);try{n.lines=await n.inFlight}finally{n.at=Date.now(),n.inFlight=void 0}return n.lines},Qt=e=>{for(let t of Z.keys())e.has(t)||Z.delete(t)},$t=async e=>{let t=e.launcher;if(!t)return Ze;if(t.type===Xe)return`This run is hosted natively rather than in a multiplexer, so there is no separate window to bring forward.`;if(t.type===j)return await h.exec(j,[`select-workspace`,`--workspace`,t.workspaceId],{timeout:w}),`Opened ${e.runKey} in cmux.`;if(!process.env.TMUX)return`Pi is not attached to tmux. Follow the output instead, or attach to the recorded tmux session manually.`;let n=t.sessionId??t.sessionName,r=await h.exec(A,[`switch-client`,`-t`,n],{timeout:w});return r.code===0?(t.paneId&&await h.exec(A,[`select-pane`,`-t`,t.paneId],{timeout:w}),`Opened ${e.runKey} in tmux.`):r.stderr.trim()||`Unable to open tmux session ${n}.`},en=async(e,t)=>{if(!ct){ct=!0;try{let n=Me(qe(e,await je(b.runDirectoryFor(e)))),r=n.length>0?void 0:await Zt(e);t.ui.setWidget(Ie,(t,i)=>({render:t=>Ge(e,r??Ke(n,i,t),i,t),invalidate:()=>{}}),{placement:`aboveEditor`})}finally{ct=!1}}},tn=async(e,t)=>{Yt(t),F=e,await en(e,t),P=setInterval(()=>{F&&en(F,t)},it),P.unref?.()},nn=async(e,t)=>{let n=e.launcher;if(!(!n||n.type===Xe)){if(n.type===A){let e=n.paneId??n.sessionId??n.sessionName;await h.exec(A,[`send-keys`,`-t`,e,`-l`,t],{timeout:w});return}await h.exec(j,[`send`,`--workspace`,n.workspaceId,t],{timeout:w})}},rn=async(e,t)=>{Gt();let n=e.launcher?.type===A||e.launcher?.type===j,r=new se(t=>nn(e,t)),i=[],a=[],o,s=!1,c=async()=>{i=Me(qe(e,await je(b.runDirectoryFor(e)))),a=await Zt(e)};await c();let l=()=>{o&&clearInterval(o),o=void 0,r.dispose()},u={interactive:n,runKey:e.runKey,teardown:l};B=u;let d=()=>{Gt(),t.ui.notify(`Closed the ${e.runKey} view. The run keeps going.`,`info`)};return t.ui.custom((f,p,ee,te)=>(o=setInterval(()=>{s||(s=!0,c().finally(()=>{s=!1,f.requestRender()}))},200),o.unref?.(),new de(f,p,{runKey:e.runKey,label:`${k.running} ${ot(e)}`,breadcrumb:`SPC › w / workflows › run`,interactive:n,footer:Ue(n),hints:We(n),snapshot:e=>({progress:Ke(i,p,e),output:a}),onUnfollow:()=>{u.handle?.unfocus(),t.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)},onClose:d,sendInput:e=>r.write(e),flushInput:()=>void r.flush(),unfollowShortcut:x,closeShortcut:S,onDispose:()=>{l(),B===u&&(B=void 0),te()}})),{...fe(n),onHandle:e=>{u.handle=e}}),t.ui.notify(n?`${e.runKey}: typing goes to the run. Press Esc twice to close, ${C(x)} to type here instead.`:`${e.runKey}: view only, because this run is hosted natively. ${C(S)} closes it.`,`info`),n?[`Showing ${e.runKey} over Pi. Typing goes to the run.`,`Pressing Escape twice quickly closes the view, and a single Escape still reaches the run.`,`${C(x)} moves typing between the run and this session,`,`and ${C(S)} closes the view. The run keeps going either way.`].join(` `):[`Showing ${e.runKey} over Pi, as a view only:`,`the run is hosted natively rather than in a multiplexer, so there is no terminal to type into.`,`Typing stays with this session, and ${C(S)} closes the view.`].join(` `)},an=async(e,t=!1)=>{if(!(St||B||F)){St=!0;try{let n=await e.ui.custom((e,t,n,r)=>new ae(e,t,{list:async()=>{let e=At(),t=(await M(b)).filter(t=>t.stage===T&&!t.stale&&c(t,e));return Ct=new Map(t.map(e=>[Y(e),e])),Promise.all(t.map(async e=>({displayName:e.displayName,executionState:e.executionState,jobs:Me(qe(e,await je(b.runDirectoryFor(e)))),key:Y(e),output:[],runKey:e.runKey,startedAt:e.startedAt,workspace:e.workspace})))},output:async e=>{let t=Ct.get(e.key);return t?Zt(t):[`Workflow is no longer active.`]}},r),{...Ne,onHandle:e=>{V=e}});if(n){let r=await Q(n);t?await un(r,e):await rn(r,e)}}finally{V=void 0,St=!1,Ct.clear()}}},Q=async(e,t=At())=>{kt(t);let n=(await M(b,e.workspace)).filter(n=>n.runKey===e.runKey&&c(n,t));if(n.length>1)throw Error(f(`Workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace in this session.`,[`Retry with the workspace from the run identity.`]));let r=n[0];if(!r)throw Error(f(`No workflow run matches ${JSON.stringify(e.runKey)} in this session.`,[`workflow_run with action status: use a run key from this session before retrying.`,`Tell the user this session did not launch that run rather than guessing a similar key. A run launched by another Pi session, or from the command line, is managed there or with the workflow-mcp CLI.`]));return r},on=async e=>{let t=(await M(b,e.workspace)).filter(t=>t.runKey===e.runKey&&t.stage===O);if(t.length>1)throw Error(f(`Failed workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace.`,[`Retry with the workspace from the recovery picker or run identity.`]));let n=t[0];if(!n)throw Error(f(`No failed workflow run matches ${JSON.stringify(e.runKey)}.`,[`Use the recovery picker to select an interrupted or failed run, or retry with its workspace.`,`Do not recover a running or completed workflow.`]));return n},sn=async e=>{let t=b.runDirectoryFor(e),n=[v(t,`changelog.md`),v(t,`context.md`),v(t,`progress.ndjson`)],r=[];for(let e of n)try{let t=await be(e,`utf-8`);r.push(`--- ${xe(e)} ---`,t.slice(-65536).trimEnd())}catch(e){if(e.code!==`ENOENT`)throw e}return r.length>0?r:[`No durable recovery evidence files are recorded for this run.`]},cn=async(e,t)=>{let n=await on(e);return await K(t).recordEvent(`doom_workflow.recovery_evidence_requested`,{outcome:`requested`}),[`Workflow: ${ot(n)}`,`--- run.json ---`,JSON.stringify(n,null,2),...await sn(n)].join(`
6
- `)},ln=async(e,t)=>{let n=nt();if(!n)return;let r=await on(e),i;try{i=Ce.parseWorkflowFile(r.workflowPath)}catch{return}if(!i[`launch-command`])return;let a=g(t.sessionManager.getSessionId()),o=await b.claimRunRecovery(r.workspace,r.runKey,a),s=r.originalRepoPath??t.cwd,c=tt([process.execPath,n,`recover-workflow`,r.runKey,`--workspace`,r.workspace,`--recovery-claim`,o.claimId,...e.runner?[`--runner`,e.runner]:[]]),l=et();try{await Be(process.execPath,[n,`launch-process`,...l?[`--launcher`,l]:[],`--name`,`${r.displayName} recover`,`--cwd`,s,`--command`,c],{cwd:s,env:$e()})}catch(e){throw await b.releaseRunRecoveryClaim(r.workspace,r.runKey,o.claimId),e}return await K(t).recordEvent(`doom_workflow.recover_delegated`,{launcher:l??`auto`,outcome:`delegated`}),`Recovering ${r.runKey} in a launcher of its own, so this session keeps its terminal. It reports back here when it ends.`},un=async(e,t)=>{let n=e;for(;;){let e=[`Open output`],r=n.executionState??T;r===T&&e.push(`Pause`),r===Re&&e.push(`Resume`),n.stage===T&&e.push(`Stop`),e.push(`Back`);let i=await le(t,{title:`Manage ${n.runKey}`,breadcrumb:`SPC › w / workflows › l / manage`,choices:e});if(!i||i===`Back`)return;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}if(i===`Open output`){await rn(n,t);return}if(i===`Stop`){if(!await t.ui.confirm(`Stop ${n.runKey}?`,`The workflow will be interrupted and will not be restarted automatically.`))continue;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}}if(i!==`Pause`&&i!==`Resume`&&i!==`Stop`)continue;if(!n.runId)throw Error(`${n.runKey} has no controllable run generation.`);let a=await Nt.execute({action:i.toLowerCase(),expectedRunId:n.runId,...i===`Stop`?{reason:`Stopped from the workflow manager.`}:{},runKey:n.runKey,workspace:n.workspace});if(a.isError)throw Error(d(a)||`Unable to ${i.toLowerCase()} workflow.`);t.ui.notify(d(a)||`${i} requested for ${n.runKey}.`,`info`),await X(t,!1),n=await Q(n)}},dn=(e,t)=>({editor:(t,n)=>e.ui.editor(t,n),input:(n,r)=>ue(e,{title:n,breadcrumb:t,...r?{fallback:r}:{}}),notify:(t,n)=>e.ui.notify(t,n),select:(n,r)=>le(e,{title:n,breadcrumb:t,choices:r})}),fn=async e=>{if(!e.hasUI){e.ui.notify(`Workflow launch needs an interactive Pi session.`,`warning`);return}let t=await oe({compatibleRunners:at,cwd:e.cwd,launch:(e,t)=>It.execute(e,t),listWorkflowsTool:_.listWorkflowsTool,parseWorkflow:e=>Ce.parseWorkflowFile(e),pickWorkflow:t=>ce(e,{title:`WORKFLOW LAUNCHER`,breadcrumb:Le,unit:`workflows`,action:`launch`,filterPlaceholder:`type to filter by name, path or tag`,emptyMessage:`No workflow definitions were found in this repository.`},t.map(e=>({key:e.path,name:e.name,detail:e.relativePath,search:[e.description,...e.tags].join(` `),value:e}))),ui:dn(e,Le)},e);t&&e.ui.notify(l(d(t)),`info`)},pn=async e=>{let t=(await M(b)).filter(e=>e.stage===O);if(t.length===0){e.ui.notify(`No failed workflows are available to recover.`,`info`);return}let n=await ce(e,{title:`WORKFLOW RECOVERY`,breadcrumb:`SPC › w / workflows › r / recover`,unit:`failed runs`,action:`recover`,filterPlaceholder:`type to filter by name, run key or failed job`,emptyMessage:`No failed workflows are available to recover.`},t.map(e=>({key:`${e.workspace}/${e.runKey}`,name:e.displayName,detail:`${e.workspace}/${e.runKey}${e.failedJob?` · ${e.failedJob}`:``}`,...e.failedJob?{search:e.failedJob}:{},value:e})));if(!n)return;let r=await on(n);if(r.stage!==O)throw Error(`${r.runKey} is no longer failed.`);let i=v(Se(we(import.meta.url)),He,`workflow-recovery`,`SKILL.md`);if(!ye(i))throw Error(`Workflow recovery skill is unavailable at ${i}.`);mn(!0),e.ui.notify(`Workflow tools enabled. The agent will investigate this failure and choose the safe next step.`,`info`),h.sendUserMessage([`Investigate recovery for this failed workflow run. This selection is permission to investigate, not permission to recover blindly.`,`Run identity: ${JSON.stringify({runKey:r.runKey,workspace:r.workspace})}`,`Read the package-owned recovery skill with the read tool at ${JSON.stringify(i)} before acting.`,`Inspect status, every relevant log source, process state, the staged run.json, and only the repair referenced by activeRepairId.`,`Choose exactly one outcome: recover, launch fresh, or defer. Never edit issue.md or repair.json, never scan historical repairs, and never pass recovery dryRun or a job override.`,`Do not recover terminal, approval-pending, blocked, or otherwise unsafe work. If evidence is incomplete, defer and explain what is missing.`,`If recovery is justified, preserve the recorded worktree and repair metadata, then verify real process and registry progress before reporting success.`].join(`
7
- `),{deliverAs:`followUp`})},mn=e=>{let t=new Set(h.getActiveTools());for(let n of p)e?t.add(n):t.delete(n);h.setActiveTools([...t]),R=e,qt(),m.onModeChange?.(e)},hn=()=>R?[`${k.completed} Workflow mode on`,`Ask for workflows in plain language: the agent can list, launch, inspect, pause, resume, stop, and recover runs.`,`${C(x)} stops following a run without stopping it.`].join(`
3
+ `),display:!0,details:{runIds:e.map(e=>e.runId??X(e))}},{triggerTurn:!0,deliverAs:`steer`});for(let t of e){let e=X(t);ut.add(e),L.delete(e),zt(e)}}catch(e){await r.recordError(`doom_workflow.terminal_delivery_failed`,e,{"workflow.pending_terminal_count":L.size})}lt=f,r.recordEvent(`doom_workflow.monitor_finished`,{duration_ms:Date.now()-i,outcome:`completed`})}catch(t){await r.recordError(`doom_workflow.monitor_failed`,t,{duration_ms:Date.now()-i}),e.ui.notify(`Workflow status refresh failed: ${t instanceof Error?t.message:String(t)}`,`warning`)}finally{Ye=!1}},Wt=e=>{P&&clearInterval(P),P=void 0,I=void 0,e.ui.setWidget(Ne,void 0)},Gt=async e=>{let t=e.launcher;if(!t)return[$e];if(Qe(e))return[Ze];if(t.type===M){let n=t.paneId??t.sessionId??t.sessionName,r=await h.exec(M,[`capture-pane`,`-p`,`-t`,n,`-S`,`-24`],{timeout:w});return r.code===0?r.stdout.trimEnd().split(`
4
+ `).slice(-24):[`Unable to read ${e.runKey}: ${r.stderr.trim()||`tmux exited ${r.code}`}`]}if(t.type===Xe)return[`This run is hosted natively and has no launcher screen to read.`];let n=await h.exec(N,[`read-screen`,`--workspace`,t.workspaceId,`--scrollback`,`--lines`,`24`],{timeout:w});return n.code===0?n.stdout.trimEnd().split(`
5
+ `).slice(-24):[`Unable to read ${e.runKey}: ${n.stderr.trim()||`cmux exited ${n.code}`}`]},Kt=new Map,qt=async e=>{let t=X(e),n=Kt.get(t)??{at:0,lines:[]};if(Kt.set(t,n),n.inFlight||Date.now()-n.at<750)return n.lines;n.inFlight=Gt(e);try{n.lines=await n.inFlight}finally{n.at=Date.now(),n.inFlight=void 0}return n.lines},Jt=e=>{for(let t of Kt.keys())e.has(t)||Kt.delete(t)},Yt=async e=>{let t=e.launcher;if(!t)return $e;if(Qe(e))return Ze;if(t.type===Xe)return`This run is hosted natively rather than in a multiplexer, so there is no separate window to bring forward.`;if(t.type===N)return await h.exec(N,[`select-workspace`,`--workspace`,t.workspaceId],{timeout:w}),`Opened ${e.runKey} in cmux.`;if(!process.env.TMUX)return`Pi is not attached to tmux. Follow the output instead, or attach to the recorded tmux session manually.`;let n=t.sessionId??t.sessionName,r=await h.exec(M,[`switch-client`,`-t`,n],{timeout:w});return r.code===0?(t.paneId&&await h.exec(M,[`select-pane`,`-t`,t.paneId],{timeout:w}),`Opened ${e.runKey} in tmux.`):r.stderr.trim()||`Unable to open tmux session ${n}.`},Xt=async(e,t)=>{if(!ct){ct=!0;try{let n=Ae(Ke(e,await ke(b.runDirectoryFor(e)))),r=n.length>0?void 0:await qt(e);t.ui.setWidget(Ne,(t,i)=>({render:t=>We(e,r??Ge(n,i,t),i,t),invalidate:()=>{}}),{placement:`aboveEditor`})}finally{ct=!1}}},Zt=async(e,t)=>{Wt(t),I=e,await Xt(e,t),P=setInterval(()=>{I&&Xt(I,t)},Je),P.unref?.()},Qt=async(e,t)=>{let n=e.launcher;if(!(!n||n.type===Xe||Qe(e))){if(n.type===M){let e=n.paneId??n.sessionId??n.sessionName;await h.exec(M,[`send-keys`,`-t`,e,`-l`,t],{timeout:w});return}await h.exec(N,[`send`,`--workspace`,n.workspaceId,t],{timeout:w})}},$t=async(e,t)=>{Ht();let n=!Qe(e)&&(e.launcher?.type===M||e.launcher?.type===N),i=new r(t=>Qt(e,t)),a=[],o=[],s,c=!1,ee=async()=>{a=Ae(Ke(e,await ke(b.runDirectoryFor(e)))),o=await qt(e)};await ee();let d=()=>{s&&clearInterval(s),s=void 0,i.dispose()},f={interactive:n,runKey:e.runKey,teardown:d};V=f;let te=()=>{Ht(),t.ui.notify(`Closed the ${e.runKey} view. The run keeps going.`,`info`)};return t.ui.custom((r,u,ne,re)=>(s=setInterval(()=>{c||(c=!0,ee().finally(()=>{c=!1,r.requestRender()}))},200),s.unref?.(),new l(r,u,{runKey:e.runKey,label:`${k.running} ${st(e)}`,breadcrumb:`SPC › w / workflows › run`,interactive:n,footer:He(n),hints:Ue(n),snapshot:e=>({progress:Ge(a,u,e),output:o}),onUnfollow:()=>{f.handle?.unfocus(),t.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)},onClose:te,sendInput:e=>i.write(e),flushInput:()=>void i.flush(),unfollowShortcut:x,closeShortcut:S,onDispose:()=>{d(),V===f&&(V=void 0),re()}})),{...u(n),onHandle:e=>{f.handle=e}}),t.ui.notify(n?`${e.runKey}: typing goes to the run. Press Esc twice to close, ${C(x)} to type here instead.`:`${e.runKey}: view only, because this run is hosted natively. ${C(S)} closes it.`,`info`),n?[`Showing ${e.runKey} over Pi. Typing goes to the run.`,`Pressing Escape twice quickly closes the view, and a single Escape still reaches the run.`,`${C(x)} moves typing between the run and this session,`,`and ${C(S)} closes the view. The run keeps going either way.`].join(` `):[`Showing ${e.runKey} over Pi, as a view only:`,`the run is hosted natively rather than in a multiplexer, so there is no terminal to type into.`,`Typing stays with this session, and ${C(S)} closes the view.`].join(` `)},en=async(e,t=!1)=>{if(!(bt||V||I)){bt=!0;try{let r=await e.ui.custom((e,t,r,i)=>new n(e,t,{list:async()=>{let e=Ot(),t=(await F(b)).filter(t=>t.stage===T&&!t.stale&&p(t,e));return xt=new Map(t.map(e=>[X(e),e])),Promise.all(t.map(async e=>({displayName:e.displayName,executionState:e.executionState,jobs:Ae(Ke(e,await ke(b.runDirectoryFor(e)))),key:X(e),output:[],runKey:e.runKey,startedAt:e.startedAt,workspace:e.workspace})))},output:async e=>{let t=xt.get(e.key);return t?qt(t):[`Workflow is no longer active.`]}},i),{...je,onHandle:e=>{yt=e}});if(r){let n=await Q(r);t?await on(n,e):await $t(n,e)}}finally{yt=void 0,bt=!1,xt.clear()}}},Q=async(e,t=Ot())=>{Dt(t);let n=(await F(b,e.workspace)).filter(n=>n.runKey===e.runKey&&p(n,t));if(n.length>1)throw Error(ue(`Workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace in this session.`,[`Retry with the workspace from the run identity.`]));let r=n[0];if(!r)throw Error(ue(`No workflow run matches ${JSON.stringify(e.runKey)} in this session.`,[`workflow_run with action status: use a run key from this session before retrying.`,`Tell the user this session did not launch that run rather than guessing a similar key. A run launched by another Pi session, or from the command line, is managed there or with the workflow-mcp CLI.`]));return r},tn=async e=>{let t=(await F(b,e.workspace)).filter(t=>t.runKey===e.runKey&&t.stage===O);if(t.length>1)throw Error(ue(`Failed workflow run key ${JSON.stringify(e.runKey)} exists in more than one workspace.`,[`Retry with the workspace from the recovery picker or run identity.`]));let n=t[0];if(!n)throw Error(ue(`No failed workflow run matches ${JSON.stringify(e.runKey)}.`,[`Use the recovery picker to select an interrupted or failed run, or retry with its workspace.`,`Do not recover a running or completed workflow.`]));return n},nn=async e=>{let t=b.runDirectoryFor(e),n=[v(t,`changelog.md`),v(t,`context.md`),v(t,`progress.ndjson`)],r=[];for(let e of n)try{let t=await ve(e,`utf-8`);r.push(`--- ${ye(e)} ---`,t.slice(-65536).trimEnd())}catch(e){if(e.code!==`ENOENT`)throw e}return r.length>0?r:[`No durable recovery evidence files are recorded for this run.`]},rn=async(e,t)=>{let n=await tn(e);return await K(t).recordEvent(`doom_workflow.recovery_evidence_requested`,{outcome:`requested`}),[`Workflow: ${st(n)}`,`--- run.json ---`,JSON.stringify(n,null,2),...await nn(n)].join(`
6
+ `)},an=async(e,t)=>{let n=it();if(!n)return;let r=await tn(e),i;try{i=xe.parseWorkflowFile(r.workflowPath)}catch{return}if(!i[`launch-command`])return;let a=g(t.sessionManager.getSessionId()),o=await b.claimRunRecovery(r.workspace,r.runKey,a),s=r.originalRepoPath??t.cwd,c=rt([process.execPath,n,`recover-workflow`,r.runKey,`--workspace`,r.workspace,`--recovery-claim`,o.claimId,...e.runner?[`--runner`,e.runner]:[]]),l=nt();try{await ze(process.execPath,[n,`launch-process`,...l?[`--launcher`,l]:[],`--name`,`${r.displayName} recover`,`--cwd`,s,`--command`,c],{cwd:s,env:tt()})}catch(e){throw await b.releaseRunRecoveryClaim(r.workspace,r.runKey,o.claimId),e}return await K(t).recordEvent(`doom_workflow.recover_delegated`,{launcher:l??`auto`,outcome:`delegated`}),`Recovering ${r.runKey} in a launcher of its own, so this session keeps its terminal. It reports back here when it ends.`},on=async(e,t)=>{let n=e;for(;;){let e=[`Open output`],r=n.executionState??T;r===T&&e.push(`Pause`),r===`paused`&&e.push(`Resume`),n.stage===T&&e.push(`Stop`),e.push(`Back`);let i=await s(t,{title:`Manage ${n.runKey}`,breadcrumb:`SPC › w / workflows › l / manage`,choices:e});if(!i||i===`Back`)return;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}if(i===`Open output`){await $t(n,t);return}if(i===`Stop`){if(!await t.ui.confirm(`Stop ${n.runKey}?`,`The workflow will be interrupted and will not be restarted automatically.`))continue;if(n=await Q(n),n.stage!==T){t.ui.notify(`${n.runKey} is no longer running.`,`warning`);return}}if(i!==`Pause`&&i!==`Resume`&&i!==`Stop`)continue;if(!n.runId)throw Error(`${n.runKey} has no controllable run generation.`);let a=await jt.execute({action:i.toLowerCase(),expectedRunId:n.runId,...i===`Stop`?{reason:`Stopped from the workflow manager.`}:{},runKey:n.runKey,workspace:n.workspace});if(a.isError)throw Error(le(a)||`Unable to ${i.toLowerCase()} workflow.`);t.ui.notify(le(a)||`${i} requested for ${n.runKey}.`,`info`),await Z(t,!1),n=await Q(n)}},sn=(e,t)=>({editor:(t,n)=>e.ui.editor(t,n),input:(n,r)=>c(e,{title:n,breadcrumb:t,...r?{fallback:r}:{}}),notify:(t,n)=>e.ui.notify(t,n),select:(n,r)=>s(e,{title:n,breadcrumb:t,choices:r})}),cn=async e=>{if(!e.hasUI){e.ui.notify(`Workflow launch needs an interactive Pi session.`,`warning`);return}let t=await me({compatibleRunners:ot,cwd:e.cwd,launch:(e,t)=>Pt.execute(e,t),listWorkflowsTool:_.listWorkflowsTool,parseWorkflow:e=>xe.parseWorkflowFile(e),pickWorkflow:t=>i(e,{title:`WORKFLOW LAUNCHER`,breadcrumb:Le,unit:`workflows`,action:`launch`,filterPlaceholder:`type to filter by name, path or tag`,emptyMessage:`No workflow definitions were found in this repository.`},t.map(e=>({key:e.path,name:e.name,detail:e.relativePath,search:[e.description,...e.tags].join(` `),value:e}))),ui:sn(e,Le)},e);t&&e.ui.notify(se(le(t)),`info`)},ln=async e=>{let t=(await F(b)).filter(e=>e.stage===O);if(t.length===0){e.ui.notify(`No failed workflows are available to recover.`,`info`);return}let n=await i(e,{title:`WORKFLOW RECOVERY`,breadcrumb:`SPC › w / workflows › r / recover`,unit:`failed runs`,action:`recover`,filterPlaceholder:`type to filter by name, run key or failed job`,emptyMessage:`No failed workflows are available to recover.`},t.map(e=>({key:`${e.workspace}/${e.runKey}`,name:e.displayName,detail:`${e.workspace}/${e.runKey}${e.failedJob?` · ${e.failedJob}`:``}`,...e.failedJob?{search:e.failedJob}:{},value:e})));if(!n)return;let r=await tn(n);if(r.stage!==O)throw Error(`${r.runKey} is no longer failed.`);let a=v(be(Se(import.meta.url)),Ve,`workflow-recovery`,`SKILL.md`);if(!_e(a))throw Error(`Workflow recovery skill is unavailable at ${a}.`);un(!0),e.ui.notify(`Workflow tools enabled. The agent will investigate this failure and choose the safe next step.`,`info`),h.sendUserMessage([`Investigate recovery for this failed workflow run. This selection is permission to investigate, not permission to recover blindly.`,`Run identity: ${JSON.stringify({runKey:r.runKey,workspace:r.workspace})}`,`Read the package-owned recovery skill with the read tool at ${JSON.stringify(a)} before acting.`,`Inspect status, every relevant log source, process state, the staged run.json, and only the repair referenced by activeRepairId.`,`Choose exactly one outcome: recover, launch fresh, or defer. Never edit issue.md or repair.json, never scan historical repairs, and never pass recovery dryRun or a job override.`,`Do not recover terminal, approval-pending, blocked, or otherwise unsafe work. If evidence is incomplete, defer and explain what is missing.`,`If recovery is justified, preserve the recorded worktree and repair metadata, then verify real process and registry progress before reporting success.`].join(`
7
+ `),{deliverAs:`followUp`})},un=e=>{let t=new Set(h.getActiveTools());for(let n of de)e?t.add(n):t.delete(n);h.setActiveTools([...t]),B=e,Ut(),m.onModeChange?.(e)},dn=()=>B?[`${k.completed} Workflow mode on`,`Ask for workflows in plain language: the agent can list, launch, inspect, pause, resume, stop, and recover runs.`,`${C(x)} stops following a run without stopping it.`].join(`
8
8
  `):[`Workflow mode off`,`Press SPC w e to give the agent its workflow tools. Runs already going are unaffected.`].join(`
9
- `),gn=async(e,t)=>{mn(e),R||Yt(t),t.ui.notify(hn(),`info`),await X(t,!1)};h.registerShortcut(x,{description:`Open workflows or move typing between a workflow run and this session`,handler:async e=>{let t=B?.handle;if(t){if(!B?.interactive){e.ui.notify(`This run is hosted natively, so the view takes no typing. Your keys stay here.`,`info`);return}t.isFocused()?(t.unfocus(),e.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)):(t.focus(),e.ui.notify(`Typing goes to ${B?.runKey}.`,`info`));return}if(F){Yt(e),e.ui.notify(`Workflow continues in the background.`,`info`);return}await an(e)}}),h.registerShortcut(S,{description:`Close the workflow run view`,handler:e=>{if(!B)return;let t=B.runKey;Gt(),e.ui.notify(`Closed the ${t} view. The run keeps going.`,`info`)}}),h.on(`resources_discover`,()=>{let e=v(Se(we(import.meta.url)),He);return{skillPaths:ye(e)?[e]:[]}});let _n=v(Se(we(import.meta.url)),He);ye(_n)&&Oe(h,{source:`@agimon-ai/doompi-workflow`,directories:[_n]}),h.registerMessageRenderer(Fe,(e,{outputPad:t},n)=>{let r=me(e.details)?e.details:void 0,i=typeof e.content==`string`?e.content:JSON.stringify(e.content);return he(r,i,t,n)}),te(h,{runTool:D,recoverTool:Mt,launchExecutor:It,controlTool:Nt,requireSessionRun:Q,requireRecoverableRun:on,readRecoveryEvidence:cn,delegateRecovery:ln,trackPendingRun:Pt,observeSession:kt,activeRunCount:async()=>mt,onLaunch:async e=>(await K(e).recordEvent(Ve,{outcome:`requested`}),X(e,!1)),followRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.follow_started`,{outcome:`started`}),await tn(n,t),`Following ${ot(n)}. Press ${C(x)} to stop following; the run keeps going.`},tailRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.tail_requested`,{outcome:`requested`}),[`Workflow: ${ot(n)}`,...await Xt(n)].join(`
10
- `)},openRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.open_requested`,{outcome:`requested`}),t.hasUI?rn(n,t):$t(n)},rejectRunner:Ft});let $=e=>e;Ee(h,{source:`@agimon-ai/doompi-workflow`,handlers:{[t]:e=>gn(!0,$(e)),[e]:e=>gn(!1,$(e)),[r]:e=>an($(e),!0),[n]:e=>fn($(e)),[i]:e=>pn($(e))},onError:(e,t,n)=>{$(n).ui.notify(e instanceof Error?e.message:String(e),`error`)}}),h.on(`session_start`,async(e,t)=>{let n=K(t);await n.recordEvent(`doom_workflow.session_started`,{mode:process.env.WORKFLOW_MCP_MODE===`on`?`on`:`off`,outcome:`started`}),xt=t;let r=g(t.sessionManager.getSessionId());kt(r),dt=Date.now(),lt=new Map,I.clear(),ut.clear(),ft=_e({bus:h.events,source:`@agimon-ai/doompi-workflow`,sessionId:r}),pt?.(),pt=ft.onNotification(ve,({mode:e})=>{let r=e===`on`;r!==R&&(mn(r),n.recordEvent(`doom_workflow.mode_changed`,{mode:e}),X(t,!1))}),mn(process.env.WORKFLOW_MCP_MODE===`on`),L?.dispose(),L=o(ft),await Ut(t),await X(t,!1),N&&clearInterval(N),N=setInterval(()=>void X(t,!0),Ye),N.unref?.()}),h.on(`session_shutdown`,async(e,t)=>{if(q.size>0){for(let e of[ze,y])e.interrupt(`SIGTERM`,{phase:`workflow`,source:`pi-session-shutdown`});await Promise.race([Promise.allSettled(q),new Promise(e=>setTimeout(e,1e4).unref?.())])}Wt(),N&&clearInterval(N),N=void 0,z&&clearInterval(z),z=void 0,_t={runs:[],failed:0},vt=void 0,bt.update(void 0),L?.dispose(),L=void 0,pt?.(),pt=void 0,ft=void 0,V?.hide(),V=void 0,St=!1,Ct.clear(),Z.clear(),Gt(),mt=0,Yt(t),t.ui.setStatus(Pe,void 0),ht&&(ht=!1,gt.update(void 0)),await G?.recordEvent(`doom_workflow.session_finished`,{"workflow.pending_count":q.size,outcome:q.size===0?`completed`:`interrupted`}),await G?.shutdown(),G=void 0})}}export{at as compatibleRunners,N as createWorkflowPiExtension,Ue as panelHint,We as panelHints,C as shortcutLabel};
9
+ `),fn=async(e,t)=>{un(e),B||Wt(t),t.ui.notify(dn(),`info`),await Z(t,!1)};h.registerShortcut(x,{description:`Open workflows or move typing between a workflow run and this session`,handler:async e=>{let t=V?.handle;if(t){if(!V?.interactive){e.ui.notify(`This run is hosted natively, so the view takes no typing. Your keys stay here.`,`info`);return}t.isFocused()?(t.unfocus(),e.ui.notify(`Typing goes to this session. The run keeps going.`,`info`)):(t.focus(),e.ui.notify(`Typing goes to ${V?.runKey}.`,`info`));return}if(I){Wt(e),e.ui.notify(`Workflow continues in the background.`,`info`);return}await en(e)}}),h.registerShortcut(S,{description:`Close the workflow run view`,handler:e=>{if(!V)return;let t=V.runKey;Ht(),e.ui.notify(`Closed the ${t} view. The run keeps going.`,`info`)}}),h.on(`resources_discover`,()=>{let e=v(be(Se(import.meta.url)),Ve);return{skillPaths:_e(e)?[e]:[]}});let pn=v(be(Se(import.meta.url)),Ve);_e(pn)&&Ee(h,{source:`@agimon-ai/doompi-workflow`,directories:[pn]}),h.registerMessageRenderer(`workflow-step`,(e,{outputPad:t},n)=>{let r=ee(e.details)?e.details:void 0,i=typeof e.content==`string`?e.content:JSON.stringify(e.content);return d(r,i,t,n)}),pe(h,{runTool:D,recoverTool:At,launchExecutor:Pt,controlTool:jt,requireSessionRun:Q,requireRecoverableRun:tn,readRecoveryEvidence:rn,delegateRecovery:an,trackPendingRun:Mt,observeSession:Dt,activeRunCount:async()=>z,onLaunch:async e=>(await K(e).recordEvent(Be,{outcome:`requested`}),Z(e,!1)),followRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.follow_started`,{outcome:`started`}),await Zt(n,t),`Following ${st(n)}. Press ${C(x)} to stop following; the run keeps going.`},tailRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.tail_requested`,{outcome:`requested`}),[`Workflow: ${st(n)}`,`Stage: ${A(n)}`,`Raw launcher output is hidden from chat. Use action follow or open to view it.`].join(`
10
+ `)},openRun:async(e,t)=>{let n=await Q(e,t.sessionManager.getSessionId());return await K(t).recordEvent(`doom_workflow.open_requested`,{outcome:`requested`}),t.hasUI?$t(n,t):Yt(n)},rejectRunner:Nt});let $=e=>e;Ce(h,{source:`@agimon-ai/doompi-workflow`,handlers:{[te]:e=>fn(!0,$(e)),[f]:e=>fn(!1,$(e)),[re]:e=>en($(e),!0),[ne]:e=>cn($(e)),[ie]:e=>ln($(e))},onError:(e,t,n)=>{$(n).ui.notify(e instanceof Error?e.message:String(e),`error`)}}),h.on(`session_start`,async(t,n)=>{let r=K(n);await r.recordEvent(`doom_workflow.session_started`,{mode:process.env.WORKFLOW_MCP_MODE===`on`?`on`:`off`,outcome:`started`}),vt=n,Tt.setUICtx(n.ui),Vt(),j&&clearInterval(j),j=void 0;let i=n.sessionManager.getSessionId();pt=Te({bus:h.events,source:ae,sessionId:i});let a=g(i);Dt(a),dt=Date.now(),mt=!1,lt=new Map,L.clear(),ut.clear(),ft=he({bus:h.events,source:`@agimon-ai/doompi-workflow`,sessionId:a}),ht?.(),ht=ft.onNotification(ge,({mode:e})=>{let t=e===`on`;t!==B&&(un(t),r.recordEvent(`doom_workflow.mode_changed`,{mode:e}),Z(n,!1))}),un(process.env.WORKFLOW_MCP_MODE===`on`),R?.dispose(),R=e(ft),await Bt(n),await Z(n,!1),mt=!0,j&&clearInterval(j),j=setInterval(()=>void Z(n,!0),qe),j.unref?.()}),h.on(`session_shutdown`,async(e,t)=>{if(q.size>0){for(let e of[Re,y])e.interrupt(`SIGTERM`,{phase:`workflow`,source:`pi-session-shutdown`});await Promise.race([Promise.allSettled(q),new Promise(e=>setTimeout(e,1e4).unref?.())])}Vt(),j&&clearInterval(j),j=void 0,R?.dispose(),R=void 0,ht?.(),ht=void 0,ft=void 0,pt=Me,mt=!1,yt?.hide(),yt=void 0,bt=!1,xt.clear(),Kt.clear(),Ht(),z=0,Wt(t),Tt.dispose(),B=!1,gt=void 0,_t.publish(Ie(!1,0)),await G?.recordEvent(`doom_workflow.session_finished`,{"workflow.pending_count":q.size,outcome:q.size===0?`completed`:`interrupted`}),await G?.shutdown(),G=void 0})}}export{ot as compatibleRunners,ct as createWorkflowPiExtension,He as panelHint,Ue as panelHints,C as shortcutLabel};
11
11
  //# sourceMappingURL=piExtension.mjs.map