@codeuxai/codeux 0.8.1 → 0.8.3

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 (38) hide show
  1. package/README.md +128 -45
  2. package/dashboard/dist/assets/{AgentAvatarScene-DAjdNR8q.js → AgentAvatarScene-CdYNjBsU.js} +1 -1
  3. package/dashboard/dist/assets/{AgentsPage-dG7HaQww.js → AgentsPage-BKJ1Zdbv.js} +2 -2
  4. package/dashboard/dist/assets/{BackgroundManager-CxjNC7ME.js → BackgroundManager-B4A4jACF.js} +1 -1
  5. package/dashboard/dist/assets/{BranchNameSchemeEditor-wfv629Ml.js → BranchNameSchemeEditor-DfnH2VYW.js} +1 -1
  6. package/dashboard/dist/assets/{BrowserPage-5eFh6pZt.js → BrowserPage-BxfAsG3M.js} +1 -1
  7. package/dashboard/dist/assets/{ChatPage-CfunB4-0.js → ChatPage-MXugCDvS.js} +1 -1
  8. package/dashboard/dist/assets/{ErrorPage-CEHtK2Hk.js → ErrorPage-Y-fEDzly.js} +1 -1
  9. package/dashboard/dist/assets/{FileBrowserPage-BOVIkrrv.js → FileBrowserPage-B4JEdW9B.js} +1 -1
  10. package/dashboard/dist/assets/{KnowledgePage-CeKSiAys.js → KnowledgePage-DVbW7o61.js} +1 -1
  11. package/dashboard/dist/assets/{ListWindowSelector-cshzGFot.js → ListWindowSelector-CPMgebqM.js} +1 -1
  12. package/dashboard/dist/assets/{MemoryPage-Cctk6NFp.js → MemoryPage-C3RWZJL5.js} +1 -1
  13. package/dashboard/dist/assets/{OverviewTelemetry-D_V7-M2H.js → OverviewTelemetry-Cy9vzhdY.js} +1 -1
  14. package/dashboard/dist/assets/{ProjectsPage-D8ICYJVv.js → ProjectsPage-RmR2LMuR.js} +1 -1
  15. package/dashboard/dist/assets/{SchedulerPage-BM0sGrEZ.js → SchedulerPage-Bg08X7Tm.js} +1 -1
  16. package/dashboard/dist/assets/{SettingsPage-BlDplYC7.js → SettingsPage-DzE_F7Kw.js} +1 -1
  17. package/dashboard/dist/assets/{SprintBoatRace-DaP10JYZ.js → SprintBoatRace-BeZOItMO.js} +1 -1
  18. package/dashboard/dist/assets/{SprintDag-DbxJdjBK.js → SprintDag-vF8f33gn.js} +1 -1
  19. package/dashboard/dist/assets/{SprintsPage-XGhd04Sn.js → SprintsPage-bRHQPfWP.js} +1 -1
  20. package/dashboard/dist/assets/{StatsPage-CrSwtTGq.js → StatsPage-BZl_tzUr.js} +1 -1
  21. package/dashboard/dist/assets/{TasksPage-DNpVBxt6.js → TasksPage-rLMlkgxX.js} +1 -1
  22. package/dashboard/dist/assets/{index-BViPTeJx.js → index-DdNzHP9F.js} +5 -5
  23. package/dashboard/dist/assets/{monaco.contribution-KtWzfVt5.js → monaco.contribution-2KFAi0p0.js} +2 -2
  24. package/dashboard/dist/assets/{tsMode-CalcmZZa.js → tsMode-dZI5bz0O.js} +1 -1
  25. package/dashboard/dist/index.html +1 -1
  26. package/dist/infrastructure/providers/cli/provider-logs/opencode-log-parser.js +174 -20
  27. package/dist/infrastructure/providers/cli/provider-runner.js +51 -0
  28. package/dist/infrastructure/providers/cli/provider-usage.js +39 -6
  29. package/dist/infrastructure/providers/cli/workspace-artifact-service.js +17 -7
  30. package/dist/repositories/execution-repository.js +77 -0
  31. package/dist/services/provider-execution-service.js +10 -1
  32. package/dist/services/runtime-startup-recovery-service.js +170 -0
  33. package/dist/services/sprint-task-dispatch-service.js +7 -0
  34. package/docs/getting-started/quickstart.md +14 -3
  35. package/docs/operations/runbook.md +2 -0
  36. package/docs/settings/configuration-and-storage.md +2 -0
  37. package/package.json +1 -1
  38. package/.code-ux/debug.log +0 -97190
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/tsMode-CalcmZZa.js","assets/editor.api2-KGjWAjnM.js","assets/rolldown-runtime-Cyuzqnbw.js","assets/editor-Br_kD0ds.css","assets/workers-BiyiPf0K.js"])))=>i.map(i=>d[i]);
2
- import{r as e}from"./rolldown-runtime-Cyuzqnbw.js";import{Ii as t}from"./index-BViPTeJx.js";import{h as n,n as r}from"./editor.api2-KGjWAjnM.js";var i=`5.9.3`,a=e({JsxEmit:()=>s,ModuleKind:()=>o,ModuleResolutionKind:()=>u,NewLineKind:()=>c,ScriptTarget:()=>l,getJavaScriptWorker:()=>_,getTypeScriptWorker:()=>g,javascriptDefaults:()=>h,typescriptDefaults:()=>m,typescriptVersion:()=>f}),o=(e=>(e[e.None=0]=`None`,e[e.CommonJS=1]=`CommonJS`,e[e.AMD=2]=`AMD`,e[e.UMD=3]=`UMD`,e[e.System=4]=`System`,e[e.ES2015=5]=`ES2015`,e[e.ESNext=99]=`ESNext`,e))(o||{}),s=(e=>(e[e.None=0]=`None`,e[e.Preserve=1]=`Preserve`,e[e.React=2]=`React`,e[e.ReactNative=3]=`ReactNative`,e[e.ReactJSX=4]=`ReactJSX`,e[e.ReactJSXDev=5]=`ReactJSXDev`,e))(s||{}),c=(e=>(e[e.CarriageReturnLineFeed=0]=`CarriageReturnLineFeed`,e[e.LineFeed=1]=`LineFeed`,e))(c||{}),l=(e=>(e[e.ES3=0]=`ES3`,e[e.ES5=1]=`ES5`,e[e.ES2015=2]=`ES2015`,e[e.ES2016=3]=`ES2016`,e[e.ES2017=4]=`ES2017`,e[e.ES2018=5]=`ES2018`,e[e.ES2019=6]=`ES2019`,e[e.ES2020=7]=`ES2020`,e[e.ESNext=99]=`ESNext`,e[e.JSON=100]=`JSON`,e[e.Latest=99]=`Latest`,e))(l||{}),u=(e=>(e[e.Classic=1]=`Classic`,e[e.NodeJs=2]=`NodeJs`,e))(u||{}),d=class{constructor(e,t,n,i,a){this._onDidChange=new r,this._onDidExtraLibsChange=new r,this._extraLibs=Object.create(null),this._removedExtraLibs=Object.create(null),this._eagerModelSync=!1,this.setCompilerOptions(e),this.setDiagnosticsOptions(t),this.setWorkerOptions(n),this.setInlayHintsOptions(i),this.setModeConfiguration(a),this._onDidExtraLibsChangeTimeout=-1}get onDidChange(){return this._onDidChange.event}get onDidExtraLibsChange(){return this._onDidExtraLibsChange.event}get modeConfiguration(){return this._modeConfiguration}get workerOptions(){return this._workerOptions}get inlayHintsOptions(){return this._inlayHintsOptions}getExtraLibs(){return this._extraLibs}addExtraLib(e,t){let n;if(n=t===void 0?`ts:extralib-${Math.random().toString(36).substring(2,15)}`:t,this._extraLibs[n]&&this._extraLibs[n].content===e)return{dispose:()=>{}};let r=1;return this._removedExtraLibs[n]&&(r=this._removedExtraLibs[n]+1),this._extraLibs[n]&&(r=this._extraLibs[n].version+1),this._extraLibs[n]={content:e,version:r},this._fireOnDidExtraLibsChangeSoon(),{dispose:()=>{let e=this._extraLibs[n];e&&e.version===r&&(delete this._extraLibs[n],this._removedExtraLibs[n]=r,this._fireOnDidExtraLibsChangeSoon())}}}setExtraLibs(e){for(let e in this._extraLibs)this._removedExtraLibs[e]=this._extraLibs[e].version;if(this._extraLibs=Object.create(null),e&&e.length>0)for(let t of e){let e=t.filePath||`ts:extralib-${Math.random().toString(36).substring(2,15)}`,n=t.content,r=1;this._removedExtraLibs[e]&&(r=this._removedExtraLibs[e]+1),this._extraLibs[e]={content:n,version:r}}this._fireOnDidExtraLibsChangeSoon()}_fireOnDidExtraLibsChangeSoon(){this._onDidExtraLibsChangeTimeout===-1&&(this._onDidExtraLibsChangeTimeout=window.setTimeout(()=>{this._onDidExtraLibsChangeTimeout=-1,this._onDidExtraLibsChange.fire(void 0)},0))}getCompilerOptions(){return this._compilerOptions}setCompilerOptions(e){this._compilerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}getDiagnosticsOptions(){return this._diagnosticsOptions}setDiagnosticsOptions(e){this._diagnosticsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setWorkerOptions(e){this._workerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setInlayHintsOptions(e){this._inlayHintsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setMaximumWorkerIdleTime(e){}setEagerModelSync(e){this._eagerModelSync=e}getEagerModelSync(){return this._eagerModelSync}setModeConfiguration(e){this._modeConfiguration=e||Object.create(null),this._onDidChange.fire(void 0)}},f=i,p={completionItems:!0,hovers:!0,documentSymbols:!0,definitions:!0,references:!0,documentHighlights:!0,rename:!0,diagnostics:!0,documentRangeFormattingEdits:!0,signatureHelp:!0,onTypeFormattingEdits:!0,codeActions:!0,inlayHints:!0},m=new d({allowNonTsExtensions:!0,target:99},{noSemanticValidation:!1,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),h=new d({allowNonTsExtensions:!0,allowJs:!0,target:99},{noSemanticValidation:!0,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),g=()=>v().then(e=>e.getTypeScriptWorker()),_=()=>v().then(e=>e.getJavaScriptWorker());function v(){return t(()=>import(`./tsMode-CalcmZZa.js`),__vite__mapDeps([0,1,2,3,4]))}n.onLanguage(`typescript`,()=>v().then(e=>e.setupTypeScript(m))),n.onLanguage(`javascript`,()=>v().then(e=>e.setupJavaScript(h)));export{m as n,a as t};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/tsMode-dZI5bz0O.js","assets/editor.api2-KGjWAjnM.js","assets/rolldown-runtime-Cyuzqnbw.js","assets/editor-Br_kD0ds.css","assets/workers-BiyiPf0K.js"])))=>i.map(i=>d[i]);
2
+ import{r as e}from"./rolldown-runtime-Cyuzqnbw.js";import{Ii as t}from"./index-DdNzHP9F.js";import{h as n,n as r}from"./editor.api2-KGjWAjnM.js";var i=`5.9.3`,a=e({JsxEmit:()=>s,ModuleKind:()=>o,ModuleResolutionKind:()=>u,NewLineKind:()=>c,ScriptTarget:()=>l,getJavaScriptWorker:()=>_,getTypeScriptWorker:()=>g,javascriptDefaults:()=>h,typescriptDefaults:()=>m,typescriptVersion:()=>f}),o=(e=>(e[e.None=0]=`None`,e[e.CommonJS=1]=`CommonJS`,e[e.AMD=2]=`AMD`,e[e.UMD=3]=`UMD`,e[e.System=4]=`System`,e[e.ES2015=5]=`ES2015`,e[e.ESNext=99]=`ESNext`,e))(o||{}),s=(e=>(e[e.None=0]=`None`,e[e.Preserve=1]=`Preserve`,e[e.React=2]=`React`,e[e.ReactNative=3]=`ReactNative`,e[e.ReactJSX=4]=`ReactJSX`,e[e.ReactJSXDev=5]=`ReactJSXDev`,e))(s||{}),c=(e=>(e[e.CarriageReturnLineFeed=0]=`CarriageReturnLineFeed`,e[e.LineFeed=1]=`LineFeed`,e))(c||{}),l=(e=>(e[e.ES3=0]=`ES3`,e[e.ES5=1]=`ES5`,e[e.ES2015=2]=`ES2015`,e[e.ES2016=3]=`ES2016`,e[e.ES2017=4]=`ES2017`,e[e.ES2018=5]=`ES2018`,e[e.ES2019=6]=`ES2019`,e[e.ES2020=7]=`ES2020`,e[e.ESNext=99]=`ESNext`,e[e.JSON=100]=`JSON`,e[e.Latest=99]=`Latest`,e))(l||{}),u=(e=>(e[e.Classic=1]=`Classic`,e[e.NodeJs=2]=`NodeJs`,e))(u||{}),d=class{constructor(e,t,n,i,a){this._onDidChange=new r,this._onDidExtraLibsChange=new r,this._extraLibs=Object.create(null),this._removedExtraLibs=Object.create(null),this._eagerModelSync=!1,this.setCompilerOptions(e),this.setDiagnosticsOptions(t),this.setWorkerOptions(n),this.setInlayHintsOptions(i),this.setModeConfiguration(a),this._onDidExtraLibsChangeTimeout=-1}get onDidChange(){return this._onDidChange.event}get onDidExtraLibsChange(){return this._onDidExtraLibsChange.event}get modeConfiguration(){return this._modeConfiguration}get workerOptions(){return this._workerOptions}get inlayHintsOptions(){return this._inlayHintsOptions}getExtraLibs(){return this._extraLibs}addExtraLib(e,t){let n;if(n=t===void 0?`ts:extralib-${Math.random().toString(36).substring(2,15)}`:t,this._extraLibs[n]&&this._extraLibs[n].content===e)return{dispose:()=>{}};let r=1;return this._removedExtraLibs[n]&&(r=this._removedExtraLibs[n]+1),this._extraLibs[n]&&(r=this._extraLibs[n].version+1),this._extraLibs[n]={content:e,version:r},this._fireOnDidExtraLibsChangeSoon(),{dispose:()=>{let e=this._extraLibs[n];e&&e.version===r&&(delete this._extraLibs[n],this._removedExtraLibs[n]=r,this._fireOnDidExtraLibsChangeSoon())}}}setExtraLibs(e){for(let e in this._extraLibs)this._removedExtraLibs[e]=this._extraLibs[e].version;if(this._extraLibs=Object.create(null),e&&e.length>0)for(let t of e){let e=t.filePath||`ts:extralib-${Math.random().toString(36).substring(2,15)}`,n=t.content,r=1;this._removedExtraLibs[e]&&(r=this._removedExtraLibs[e]+1),this._extraLibs[e]={content:n,version:r}}this._fireOnDidExtraLibsChangeSoon()}_fireOnDidExtraLibsChangeSoon(){this._onDidExtraLibsChangeTimeout===-1&&(this._onDidExtraLibsChangeTimeout=window.setTimeout(()=>{this._onDidExtraLibsChangeTimeout=-1,this._onDidExtraLibsChange.fire(void 0)},0))}getCompilerOptions(){return this._compilerOptions}setCompilerOptions(e){this._compilerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}getDiagnosticsOptions(){return this._diagnosticsOptions}setDiagnosticsOptions(e){this._diagnosticsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setWorkerOptions(e){this._workerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setInlayHintsOptions(e){this._inlayHintsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setMaximumWorkerIdleTime(e){}setEagerModelSync(e){this._eagerModelSync=e}getEagerModelSync(){return this._eagerModelSync}setModeConfiguration(e){this._modeConfiguration=e||Object.create(null),this._onDidChange.fire(void 0)}},f=i,p={completionItems:!0,hovers:!0,documentSymbols:!0,definitions:!0,references:!0,documentHighlights:!0,rename:!0,diagnostics:!0,documentRangeFormattingEdits:!0,signatureHelp:!0,onTypeFormattingEdits:!0,codeActions:!0,inlayHints:!0},m=new d({allowNonTsExtensions:!0,target:99},{noSemanticValidation:!1,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),h=new d({allowNonTsExtensions:!0,allowJs:!0,target:99},{noSemanticValidation:!0,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),g=()=>v().then(e=>e.getTypeScriptWorker()),_=()=>v().then(e=>e.getJavaScriptWorker());function v(){return t(()=>import(`./tsMode-dZI5bz0O.js`),__vite__mapDeps([0,1,2,3,4]))}n.onLanguage(`typescript`,()=>v().then(e=>e.setupTypeScript(m))),n.onLanguage(`javascript`,()=>v().then(e=>e.setupJavaScript(h)));export{m as n,a as t};
@@ -1,4 +1,4 @@
1
- import{a as e,c as t,f as n,h as r,o as i,p as a}from"./editor.api2-KGjWAjnM.js";import{n as o}from"./monaco.contribution-KtWzfVt5.js";import{t as s}from"./workers-BiyiPf0K.js";var c=class{constructor(e,t){this._modeId=e,this._defaults=t,this._worker=null,this._client=null,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker()),this._updateExtraLibsToken=0,this._extraLibsChangeListener=this._defaults.onDidExtraLibsChange(()=>this._updateExtraLibs())}dispose(){this._configChangeListener.dispose(),this._extraLibsChangeListener.dispose(),this._stopWorker()}_stopWorker(){this._worker&&=(this._worker.dispose(),null),this._client=null}async _updateExtraLibs(){if(!this._worker)return;let e=++this._updateExtraLibsToken,t=await this._worker.getProxy();this._updateExtraLibsToken===e&&t.updateExtraLibs(this._defaults.getExtraLibs())}_getClient(){return this._client||=(async()=>(this._worker=s({moduleId:`vs/language/typescript/tsWorker`,createWorker:()=>new Worker(new URL(`/assets/ts.worker-B0J26iPs.js`,``+import.meta.url),{type:`module`}),label:this._modeId,keepIdleModels:!0,createData:{compilerOptions:this._defaults.getCompilerOptions(),extraLibs:this._defaults.getExtraLibs(),customWorkerPath:this._defaults.workerOptions.customWorkerPath,inlayHintsOptions:this._defaults.inlayHintsOptions}}),this._defaults.getEagerModelSync()?await this._worker.withSyncedResources(a.getModels().filter(e=>e.getLanguageId()===this._modeId).map(e=>e.uri)):await this._worker.getProxy()))(),this._client}async getLanguageServiceWorker(...e){let t=await this._getClient();return this._worker&&await this._worker.withSyncedResources(e),t}},l={};l[`lib.d.ts`]=!0,l[`lib.decorators.d.ts`]=!0,l[`lib.decorators.legacy.d.ts`]=!0,l[`lib.dom.asynciterable.d.ts`]=!0,l[`lib.dom.d.ts`]=!0,l[`lib.dom.iterable.d.ts`]=!0,l[`lib.es2015.collection.d.ts`]=!0,l[`lib.es2015.core.d.ts`]=!0,l[`lib.es2015.d.ts`]=!0,l[`lib.es2015.generator.d.ts`]=!0,l[`lib.es2015.iterable.d.ts`]=!0,l[`lib.es2015.promise.d.ts`]=!0,l[`lib.es2015.proxy.d.ts`]=!0,l[`lib.es2015.reflect.d.ts`]=!0,l[`lib.es2015.symbol.d.ts`]=!0,l[`lib.es2015.symbol.wellknown.d.ts`]=!0,l[`lib.es2016.array.include.d.ts`]=!0,l[`lib.es2016.d.ts`]=!0,l[`lib.es2016.full.d.ts`]=!0,l[`lib.es2016.intl.d.ts`]=!0,l[`lib.es2017.arraybuffer.d.ts`]=!0,l[`lib.es2017.d.ts`]=!0,l[`lib.es2017.date.d.ts`]=!0,l[`lib.es2017.full.d.ts`]=!0,l[`lib.es2017.intl.d.ts`]=!0,l[`lib.es2017.object.d.ts`]=!0,l[`lib.es2017.sharedmemory.d.ts`]=!0,l[`lib.es2017.string.d.ts`]=!0,l[`lib.es2017.typedarrays.d.ts`]=!0,l[`lib.es2018.asyncgenerator.d.ts`]=!0,l[`lib.es2018.asynciterable.d.ts`]=!0,l[`lib.es2018.d.ts`]=!0,l[`lib.es2018.full.d.ts`]=!0,l[`lib.es2018.intl.d.ts`]=!0,l[`lib.es2018.promise.d.ts`]=!0,l[`lib.es2018.regexp.d.ts`]=!0,l[`lib.es2019.array.d.ts`]=!0,l[`lib.es2019.d.ts`]=!0,l[`lib.es2019.full.d.ts`]=!0,l[`lib.es2019.intl.d.ts`]=!0,l[`lib.es2019.object.d.ts`]=!0,l[`lib.es2019.string.d.ts`]=!0,l[`lib.es2019.symbol.d.ts`]=!0,l[`lib.es2020.bigint.d.ts`]=!0,l[`lib.es2020.d.ts`]=!0,l[`lib.es2020.date.d.ts`]=!0,l[`lib.es2020.full.d.ts`]=!0,l[`lib.es2020.intl.d.ts`]=!0,l[`lib.es2020.number.d.ts`]=!0,l[`lib.es2020.promise.d.ts`]=!0,l[`lib.es2020.sharedmemory.d.ts`]=!0,l[`lib.es2020.string.d.ts`]=!0,l[`lib.es2020.symbol.wellknown.d.ts`]=!0,l[`lib.es2021.d.ts`]=!0,l[`lib.es2021.full.d.ts`]=!0,l[`lib.es2021.intl.d.ts`]=!0,l[`lib.es2021.promise.d.ts`]=!0,l[`lib.es2021.string.d.ts`]=!0,l[`lib.es2021.weakref.d.ts`]=!0,l[`lib.es2022.array.d.ts`]=!0,l[`lib.es2022.d.ts`]=!0,l[`lib.es2022.error.d.ts`]=!0,l[`lib.es2022.full.d.ts`]=!0,l[`lib.es2022.intl.d.ts`]=!0,l[`lib.es2022.object.d.ts`]=!0,l[`lib.es2022.regexp.d.ts`]=!0,l[`lib.es2022.string.d.ts`]=!0,l[`lib.es2023.array.d.ts`]=!0,l[`lib.es2023.collection.d.ts`]=!0,l[`lib.es2023.d.ts`]=!0,l[`lib.es2023.full.d.ts`]=!0,l[`lib.es2023.intl.d.ts`]=!0,l[`lib.es2024.arraybuffer.d.ts`]=!0,l[`lib.es2024.collection.d.ts`]=!0,l[`lib.es2024.d.ts`]=!0,l[`lib.es2024.full.d.ts`]=!0,l[`lib.es2024.object.d.ts`]=!0,l[`lib.es2024.promise.d.ts`]=!0,l[`lib.es2024.regexp.d.ts`]=!0,l[`lib.es2024.sharedmemory.d.ts`]=!0,l[`lib.es2024.string.d.ts`]=!0,l[`lib.es5.d.ts`]=!0,l[`lib.es6.d.ts`]=!0,l[`lib.esnext.array.d.ts`]=!0,l[`lib.esnext.collection.d.ts`]=!0,l[`lib.esnext.d.ts`]=!0,l[`lib.esnext.decorators.d.ts`]=!0,l[`lib.esnext.disposable.d.ts`]=!0,l[`lib.esnext.error.d.ts`]=!0,l[`lib.esnext.float16.d.ts`]=!0,l[`lib.esnext.full.d.ts`]=!0,l[`lib.esnext.intl.d.ts`]=!0,l[`lib.esnext.iterator.d.ts`]=!0,l[`lib.esnext.promise.d.ts`]=!0,l[`lib.esnext.sharedmemory.d.ts`]=!0,l[`lib.scripthost.d.ts`]=!0,l[`lib.webworker.asynciterable.d.ts`]=!0,l[`lib.webworker.d.ts`]=!0,l[`lib.webworker.importscripts.d.ts`]=!0,l[`lib.webworker.iterable.d.ts`]=!0;function u(e,t,n=0){if(typeof e==`string`)return e;if(e===void 0)return``;let r=``;if(n){r+=t;for(let e=0;e<n;e++)r+=` `}if(r+=e.messageText,n++,e.next)for(let i of e.next)r+=u(i,t,n);return r}function d(e){return e?e.map(e=>e.text).join(``):``}var f=class{constructor(e){this._worker=e}_textSpanToRange(e,t){let n=e.getPositionAt(t.start),r=e.getPositionAt(t.start+t.length),{lineNumber:i,column:a}=n,{lineNumber:o,column:s}=r;return{startLineNumber:i,startColumn:a,endLineNumber:o,endColumn:s}}},p=class{constructor(e){this._worker=e,this._libFiles={},this._hasFetchedLibFiles=!1,this._fetchLibFilesPromise=null}isLibFile(e){return e&&e.path.indexOf(`/lib.`)===0?!!l[e.path.slice(1)]:!1}getOrCreateModel(e){let t=n.parse(e),r=a.getModel(t);if(r)return r;if(this.isLibFile(t)&&this._hasFetchedLibFiles)return a.createModel(this._libFiles[t.path.slice(1)],`typescript`,t);let i=o.getExtraLibs()[e];return i?a.createModel(i.content,`typescript`,t):null}_containsLibFile(e){for(let t of e)if(this.isLibFile(t))return!0;return!1}async fetchLibFilesIfNecessary(e){this._containsLibFile(e)&&await this._fetchLibFiles()}_fetchLibFiles(){return this._fetchLibFilesPromise||=this._worker().then(e=>e.getLibFiles()).then(e=>{this._hasFetchedLibFiles=!0,this._libFiles=e}),this._fetchLibFilesPromise}},m=class extends f{constructor(e,t,n,r){super(r),this._libFiles=e,this._defaults=t,this._selector=n,this._disposables=[],this._listener=Object.create(null);let i=e=>{if(e.getLanguageId()!==n)return;let t=()=>{let{onlyVisible:t}=this._defaults.getDiagnosticsOptions();t?e.isAttachedToEditor()&&this._doValidate(e):this._doValidate(e)},r,i=e.onDidChangeContent(()=>{clearTimeout(r),r=window.setTimeout(t,500)}),o=e.onDidChangeAttached(()=>{let{onlyVisible:n}=this._defaults.getDiagnosticsOptions();n&&(e.isAttachedToEditor()?t():a.setModelMarkers(e,this._selector,[]))});this._listener[e.uri.toString()]={dispose(){i.dispose(),o.dispose(),clearTimeout(r)}},t()},o=e=>{a.setModelMarkers(e,this._selector,[]);let t=e.uri.toString();this._listener[t]&&(this._listener[t].dispose(),delete this._listener[t])};this._disposables.push(a.onDidCreateModel(e=>i(e))),this._disposables.push(a.onWillDisposeModel(o)),this._disposables.push(a.onDidChangeModelLanguage(e=>{o(e.model),i(e.model)})),this._disposables.push({dispose(){for(let e of a.getModels())o(e)}});let s=()=>{for(let e of a.getModels())o(e),i(e)};this._disposables.push(this._defaults.onDidChange(s)),this._disposables.push(this._defaults.onDidExtraLibsChange(s)),a.getModels().forEach(e=>i(e))}dispose(){this._disposables.forEach(e=>e&&e.dispose()),this._disposables=[]}async _doValidate(e){let t=await this._worker(e.uri);if(e.isDisposed())return;let r=[],{noSyntaxValidation:i,noSemanticValidation:o,noSuggestionDiagnostics:s}=this._defaults.getDiagnosticsOptions();i||r.push(t.getSyntacticDiagnostics(e.uri.toString())),o||r.push(t.getSemanticDiagnostics(e.uri.toString())),s||r.push(t.getSuggestionDiagnostics(e.uri.toString()));let c=await Promise.all(r);if(!c||e.isDisposed())return;let l=c.reduce((e,t)=>t.concat(e),[]).filter(e=>(this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore||[]).indexOf(e.code)===-1),u=l.map(e=>e.relatedInformation||[]).reduce((e,t)=>t.concat(e),[]).map(e=>e.file?n.parse(e.file.fileName):null);await this._libFiles.fetchLibFilesIfNecessary(u),!e.isDisposed()&&a.setModelMarkers(e,this._selector,l.map(t=>this._convertDiagnostics(e,t)))}_convertDiagnostics(e,t){let n=t.start||0,r=t.length||1,{lineNumber:a,column:o}=e.getPositionAt(n),{lineNumber:s,column:c}=e.getPositionAt(n+r),l=[];return t.reportsUnnecessary&&l.push(i.Unnecessary),t.reportsDeprecated&&l.push(i.Deprecated),{severity:this._tsDiagnosticCategoryToMarkerSeverity(t.category),startLineNumber:a,startColumn:o,endLineNumber:s,endColumn:c,message:u(t.messageText,`
1
+ import{a as e,c as t,f as n,h as r,o as i,p as a}from"./editor.api2-KGjWAjnM.js";import{n as o}from"./monaco.contribution-2KFAi0p0.js";import{t as s}from"./workers-BiyiPf0K.js";var c=class{constructor(e,t){this._modeId=e,this._defaults=t,this._worker=null,this._client=null,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker()),this._updateExtraLibsToken=0,this._extraLibsChangeListener=this._defaults.onDidExtraLibsChange(()=>this._updateExtraLibs())}dispose(){this._configChangeListener.dispose(),this._extraLibsChangeListener.dispose(),this._stopWorker()}_stopWorker(){this._worker&&=(this._worker.dispose(),null),this._client=null}async _updateExtraLibs(){if(!this._worker)return;let e=++this._updateExtraLibsToken,t=await this._worker.getProxy();this._updateExtraLibsToken===e&&t.updateExtraLibs(this._defaults.getExtraLibs())}_getClient(){return this._client||=(async()=>(this._worker=s({moduleId:`vs/language/typescript/tsWorker`,createWorker:()=>new Worker(new URL(`/assets/ts.worker-B0J26iPs.js`,``+import.meta.url),{type:`module`}),label:this._modeId,keepIdleModels:!0,createData:{compilerOptions:this._defaults.getCompilerOptions(),extraLibs:this._defaults.getExtraLibs(),customWorkerPath:this._defaults.workerOptions.customWorkerPath,inlayHintsOptions:this._defaults.inlayHintsOptions}}),this._defaults.getEagerModelSync()?await this._worker.withSyncedResources(a.getModels().filter(e=>e.getLanguageId()===this._modeId).map(e=>e.uri)):await this._worker.getProxy()))(),this._client}async getLanguageServiceWorker(...e){let t=await this._getClient();return this._worker&&await this._worker.withSyncedResources(e),t}},l={};l[`lib.d.ts`]=!0,l[`lib.decorators.d.ts`]=!0,l[`lib.decorators.legacy.d.ts`]=!0,l[`lib.dom.asynciterable.d.ts`]=!0,l[`lib.dom.d.ts`]=!0,l[`lib.dom.iterable.d.ts`]=!0,l[`lib.es2015.collection.d.ts`]=!0,l[`lib.es2015.core.d.ts`]=!0,l[`lib.es2015.d.ts`]=!0,l[`lib.es2015.generator.d.ts`]=!0,l[`lib.es2015.iterable.d.ts`]=!0,l[`lib.es2015.promise.d.ts`]=!0,l[`lib.es2015.proxy.d.ts`]=!0,l[`lib.es2015.reflect.d.ts`]=!0,l[`lib.es2015.symbol.d.ts`]=!0,l[`lib.es2015.symbol.wellknown.d.ts`]=!0,l[`lib.es2016.array.include.d.ts`]=!0,l[`lib.es2016.d.ts`]=!0,l[`lib.es2016.full.d.ts`]=!0,l[`lib.es2016.intl.d.ts`]=!0,l[`lib.es2017.arraybuffer.d.ts`]=!0,l[`lib.es2017.d.ts`]=!0,l[`lib.es2017.date.d.ts`]=!0,l[`lib.es2017.full.d.ts`]=!0,l[`lib.es2017.intl.d.ts`]=!0,l[`lib.es2017.object.d.ts`]=!0,l[`lib.es2017.sharedmemory.d.ts`]=!0,l[`lib.es2017.string.d.ts`]=!0,l[`lib.es2017.typedarrays.d.ts`]=!0,l[`lib.es2018.asyncgenerator.d.ts`]=!0,l[`lib.es2018.asynciterable.d.ts`]=!0,l[`lib.es2018.d.ts`]=!0,l[`lib.es2018.full.d.ts`]=!0,l[`lib.es2018.intl.d.ts`]=!0,l[`lib.es2018.promise.d.ts`]=!0,l[`lib.es2018.regexp.d.ts`]=!0,l[`lib.es2019.array.d.ts`]=!0,l[`lib.es2019.d.ts`]=!0,l[`lib.es2019.full.d.ts`]=!0,l[`lib.es2019.intl.d.ts`]=!0,l[`lib.es2019.object.d.ts`]=!0,l[`lib.es2019.string.d.ts`]=!0,l[`lib.es2019.symbol.d.ts`]=!0,l[`lib.es2020.bigint.d.ts`]=!0,l[`lib.es2020.d.ts`]=!0,l[`lib.es2020.date.d.ts`]=!0,l[`lib.es2020.full.d.ts`]=!0,l[`lib.es2020.intl.d.ts`]=!0,l[`lib.es2020.number.d.ts`]=!0,l[`lib.es2020.promise.d.ts`]=!0,l[`lib.es2020.sharedmemory.d.ts`]=!0,l[`lib.es2020.string.d.ts`]=!0,l[`lib.es2020.symbol.wellknown.d.ts`]=!0,l[`lib.es2021.d.ts`]=!0,l[`lib.es2021.full.d.ts`]=!0,l[`lib.es2021.intl.d.ts`]=!0,l[`lib.es2021.promise.d.ts`]=!0,l[`lib.es2021.string.d.ts`]=!0,l[`lib.es2021.weakref.d.ts`]=!0,l[`lib.es2022.array.d.ts`]=!0,l[`lib.es2022.d.ts`]=!0,l[`lib.es2022.error.d.ts`]=!0,l[`lib.es2022.full.d.ts`]=!0,l[`lib.es2022.intl.d.ts`]=!0,l[`lib.es2022.object.d.ts`]=!0,l[`lib.es2022.regexp.d.ts`]=!0,l[`lib.es2022.string.d.ts`]=!0,l[`lib.es2023.array.d.ts`]=!0,l[`lib.es2023.collection.d.ts`]=!0,l[`lib.es2023.d.ts`]=!0,l[`lib.es2023.full.d.ts`]=!0,l[`lib.es2023.intl.d.ts`]=!0,l[`lib.es2024.arraybuffer.d.ts`]=!0,l[`lib.es2024.collection.d.ts`]=!0,l[`lib.es2024.d.ts`]=!0,l[`lib.es2024.full.d.ts`]=!0,l[`lib.es2024.object.d.ts`]=!0,l[`lib.es2024.promise.d.ts`]=!0,l[`lib.es2024.regexp.d.ts`]=!0,l[`lib.es2024.sharedmemory.d.ts`]=!0,l[`lib.es2024.string.d.ts`]=!0,l[`lib.es5.d.ts`]=!0,l[`lib.es6.d.ts`]=!0,l[`lib.esnext.array.d.ts`]=!0,l[`lib.esnext.collection.d.ts`]=!0,l[`lib.esnext.d.ts`]=!0,l[`lib.esnext.decorators.d.ts`]=!0,l[`lib.esnext.disposable.d.ts`]=!0,l[`lib.esnext.error.d.ts`]=!0,l[`lib.esnext.float16.d.ts`]=!0,l[`lib.esnext.full.d.ts`]=!0,l[`lib.esnext.intl.d.ts`]=!0,l[`lib.esnext.iterator.d.ts`]=!0,l[`lib.esnext.promise.d.ts`]=!0,l[`lib.esnext.sharedmemory.d.ts`]=!0,l[`lib.scripthost.d.ts`]=!0,l[`lib.webworker.asynciterable.d.ts`]=!0,l[`lib.webworker.d.ts`]=!0,l[`lib.webworker.importscripts.d.ts`]=!0,l[`lib.webworker.iterable.d.ts`]=!0;function u(e,t,n=0){if(typeof e==`string`)return e;if(e===void 0)return``;let r=``;if(n){r+=t;for(let e=0;e<n;e++)r+=` `}if(r+=e.messageText,n++,e.next)for(let i of e.next)r+=u(i,t,n);return r}function d(e){return e?e.map(e=>e.text).join(``):``}var f=class{constructor(e){this._worker=e}_textSpanToRange(e,t){let n=e.getPositionAt(t.start),r=e.getPositionAt(t.start+t.length),{lineNumber:i,column:a}=n,{lineNumber:o,column:s}=r;return{startLineNumber:i,startColumn:a,endLineNumber:o,endColumn:s}}},p=class{constructor(e){this._worker=e,this._libFiles={},this._hasFetchedLibFiles=!1,this._fetchLibFilesPromise=null}isLibFile(e){return e&&e.path.indexOf(`/lib.`)===0?!!l[e.path.slice(1)]:!1}getOrCreateModel(e){let t=n.parse(e),r=a.getModel(t);if(r)return r;if(this.isLibFile(t)&&this._hasFetchedLibFiles)return a.createModel(this._libFiles[t.path.slice(1)],`typescript`,t);let i=o.getExtraLibs()[e];return i?a.createModel(i.content,`typescript`,t):null}_containsLibFile(e){for(let t of e)if(this.isLibFile(t))return!0;return!1}async fetchLibFilesIfNecessary(e){this._containsLibFile(e)&&await this._fetchLibFiles()}_fetchLibFiles(){return this._fetchLibFilesPromise||=this._worker().then(e=>e.getLibFiles()).then(e=>{this._hasFetchedLibFiles=!0,this._libFiles=e}),this._fetchLibFilesPromise}},m=class extends f{constructor(e,t,n,r){super(r),this._libFiles=e,this._defaults=t,this._selector=n,this._disposables=[],this._listener=Object.create(null);let i=e=>{if(e.getLanguageId()!==n)return;let t=()=>{let{onlyVisible:t}=this._defaults.getDiagnosticsOptions();t?e.isAttachedToEditor()&&this._doValidate(e):this._doValidate(e)},r,i=e.onDidChangeContent(()=>{clearTimeout(r),r=window.setTimeout(t,500)}),o=e.onDidChangeAttached(()=>{let{onlyVisible:n}=this._defaults.getDiagnosticsOptions();n&&(e.isAttachedToEditor()?t():a.setModelMarkers(e,this._selector,[]))});this._listener[e.uri.toString()]={dispose(){i.dispose(),o.dispose(),clearTimeout(r)}},t()},o=e=>{a.setModelMarkers(e,this._selector,[]);let t=e.uri.toString();this._listener[t]&&(this._listener[t].dispose(),delete this._listener[t])};this._disposables.push(a.onDidCreateModel(e=>i(e))),this._disposables.push(a.onWillDisposeModel(o)),this._disposables.push(a.onDidChangeModelLanguage(e=>{o(e.model),i(e.model)})),this._disposables.push({dispose(){for(let e of a.getModels())o(e)}});let s=()=>{for(let e of a.getModels())o(e),i(e)};this._disposables.push(this._defaults.onDidChange(s)),this._disposables.push(this._defaults.onDidExtraLibsChange(s)),a.getModels().forEach(e=>i(e))}dispose(){this._disposables.forEach(e=>e&&e.dispose()),this._disposables=[]}async _doValidate(e){let t=await this._worker(e.uri);if(e.isDisposed())return;let r=[],{noSyntaxValidation:i,noSemanticValidation:o,noSuggestionDiagnostics:s}=this._defaults.getDiagnosticsOptions();i||r.push(t.getSyntacticDiagnostics(e.uri.toString())),o||r.push(t.getSemanticDiagnostics(e.uri.toString())),s||r.push(t.getSuggestionDiagnostics(e.uri.toString()));let c=await Promise.all(r);if(!c||e.isDisposed())return;let l=c.reduce((e,t)=>t.concat(e),[]).filter(e=>(this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore||[]).indexOf(e.code)===-1),u=l.map(e=>e.relatedInformation||[]).reduce((e,t)=>t.concat(e),[]).map(e=>e.file?n.parse(e.file.fileName):null);await this._libFiles.fetchLibFilesIfNecessary(u),!e.isDisposed()&&a.setModelMarkers(e,this._selector,l.map(t=>this._convertDiagnostics(e,t)))}_convertDiagnostics(e,t){let n=t.start||0,r=t.length||1,{lineNumber:a,column:o}=e.getPositionAt(n),{lineNumber:s,column:c}=e.getPositionAt(n+r),l=[];return t.reportsUnnecessary&&l.push(i.Unnecessary),t.reportsDeprecated&&l.push(i.Deprecated),{severity:this._tsDiagnosticCategoryToMarkerSeverity(t.category),startLineNumber:a,startColumn:o,endLineNumber:s,endColumn:c,message:u(t.messageText,`
2
2
  `),code:t.code.toString(),tags:l,relatedInformation:this._convertRelatedInformation(e,t.relatedInformation)}}_convertRelatedInformation(e,t){if(!t)return[];let n=[];return t.forEach(t=>{let r=e;if(t.file&&(r=this._libFiles.getOrCreateModel(t.file.fileName)),!r)return;let i=t.start||0,a=t.length||1,{lineNumber:o,column:s}=r.getPositionAt(i),{lineNumber:c,column:l}=r.getPositionAt(i+a);n.push({resource:r.uri,startLineNumber:o,startColumn:s,endLineNumber:c,endColumn:l,message:u(t.messageText,`
3
3
  `)})}),n}_tsDiagnosticCategoryToMarkerSeverity(t){switch(t){case 1:return e.Error;case 3:return e.Info;case 0:return e.Warning;case 2:return e.Hint}return e.Info}},h=class e extends f{get triggerCharacters(){return[`.`]}async provideCompletionItems(n,i,a,o){let s=n.getWordUntilPosition(i),c=new t(i.lineNumber,s.startColumn,i.lineNumber,s.endColumn),l=n.uri,u=n.getOffsetAt(i),d=await this._worker(l);if(n.isDisposed())return;let f=await d.getCompletionsAtPosition(l.toString(),u);if(!(!f||n.isDisposed()))return{suggestions:f.entries.map(a=>{let o=c;if(a.replacementSpan){let e=n.getPositionAt(a.replacementSpan.start),r=n.getPositionAt(a.replacementSpan.start+a.replacementSpan.length);o=new t(e.lineNumber,e.column,r.lineNumber,r.column)}let s=[];return a.kindModifiers!==void 0&&a.kindModifiers.indexOf(`deprecated`)!==-1&&s.push(r.CompletionItemTag.Deprecated),{uri:l,position:i,offset:u,range:o,label:a.name,insertText:a.name,sortText:a.sortText,kind:e.convertKind(a.kind),tags:s}})}}async resolveCompletionItem(t,n){let r=t,i=r.uri,a=r.position,o=r.offset,s=await(await this._worker(i)).getCompletionEntryDetails(i.toString(),o,r.label);return s?{uri:i,position:a,label:s.name,kind:e.convertKind(s.kind),detail:d(s.displayParts),documentation:{value:e.createDocumentationString(s)}}:r}static convertKind(e){switch(e){case C.primitiveType:case C.keyword:return r.CompletionItemKind.Keyword;case C.variable:case C.localVariable:return r.CompletionItemKind.Variable;case C.memberVariable:case C.memberGetAccessor:case C.memberSetAccessor:return r.CompletionItemKind.Field;case C.function:case C.memberFunction:case C.constructSignature:case C.callSignature:case C.indexSignature:return r.CompletionItemKind.Function;case C.enum:return r.CompletionItemKind.Enum;case C.module:return r.CompletionItemKind.Module;case C.class:return r.CompletionItemKind.Class;case C.interface:return r.CompletionItemKind.Interface;case C.warning:return r.CompletionItemKind.File}return r.CompletionItemKind.Property}static createDocumentationString(e){let t=d(e.documentation);if(e.tags)for(let n of e.tags)t+=`
4
4
 
@@ -11,7 +11,7 @@
11
11
  <link
12
12
  href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=JetBrains+Mono:wght@400;500;600&family=Outfit:wght@300;400;500;600;700;800;900&display=swap"
13
13
  rel="stylesheet" />
14
- <script type="module" crossorigin src="/assets/index-BViPTeJx.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-DdNzHP9F.js"></script>
15
15
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Cyuzqnbw.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/vendor-Dl0fhdV0.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/project-data-BSCsMQnj.js">
@@ -14,23 +14,118 @@ function stringify(value) {
14
14
  return String(value);
15
15
  }
16
16
  }
17
+ /**
18
+ * Reads OpenCode's token shape: `{ input, output, reasoning, cache: { read,
19
+ * write } }` (carried by both `step-finish` parts and assistant messages).
20
+ * Falls back to OpenAI-style aliases so a future schema tweak still resolves.
21
+ */
22
+ function readOpenCodeTokens(tokens) {
23
+ const cache = asRecord(tokens.cache);
24
+ return {
25
+ input: toNumber(tokens.input ?? tokens.inputTokens ?? tokens.promptTokens ?? tokens.prompt_tokens ?? 0),
26
+ output: toNumber(tokens.output ?? tokens.outputTokens ?? tokens.completionTokens ?? tokens.completion_tokens ?? 0),
27
+ reasoning: toNumber(tokens.reasoning ?? tokens.reasoningTokens ?? tokens.reasoning_tokens ?? 0),
28
+ cacheRead: toNumber(cache?.read ?? tokens.cache_read ?? tokens.cachedInputTokens ?? 0),
29
+ cacheWrite: toNumber(cache?.write ?? tokens.cache_write ?? 0),
30
+ };
31
+ }
32
+ const SESSION_ID_RE = /^ses_[A-Za-z0-9]+$/;
33
+ /** Extracts the first balanced, string-aware JSON object from a blob of text,
34
+ * so the export JSON survives any incidental wrapper output on the stream. */
35
+ function extractFirstJsonObject(text) {
36
+ const start = text.indexOf("{");
37
+ if (start < 0) {
38
+ return null;
39
+ }
40
+ let depth = 0;
41
+ let inString = false;
42
+ let escaped = false;
43
+ for (let i = start; i < text.length; i += 1) {
44
+ const ch = text[i];
45
+ if (inString) {
46
+ if (escaped)
47
+ escaped = false;
48
+ else if (ch === "\\")
49
+ escaped = true;
50
+ else if (ch === "\"")
51
+ inString = false;
52
+ continue;
53
+ }
54
+ if (ch === "\"")
55
+ inString = true;
56
+ else if (ch === "{")
57
+ depth += 1;
58
+ else if (ch === "}") {
59
+ depth -= 1;
60
+ if (depth === 0) {
61
+ return text.slice(start, i + 1);
62
+ }
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ /**
68
+ * Parses the JSON emitted by `opencode export <sessionID>`. OpenCode does not
69
+ * report token usage on the `run --format json` stdout stream — usage lives in
70
+ * its session store, surfaced here as `info.tokens` (the session-cumulative
71
+ * `{ input, output, reasoning, cache: { read, write } }`) plus `info.cost`.
72
+ */
73
+ export function parseOpenCodeExport(exportStdout) {
74
+ // Anchor on the export's `info` object so any wrapper/bootstrap stdout that
75
+ // happens to contain braces can't be mistaken for the payload.
76
+ const infoIndex = exportStdout.search(/\{\s*"info"\s*:/);
77
+ const searchText = infoIndex >= 0 ? exportStdout.slice(infoIndex) : exportStdout;
78
+ const objectText = extractFirstJsonObject(searchText);
79
+ if (!objectText) {
80
+ return null;
81
+ }
82
+ const root = parseJsonObject(objectText);
83
+ const info = asRecord(root?.info);
84
+ const tokens = asRecord(info?.tokens);
85
+ if (!tokens) {
86
+ return null;
87
+ }
88
+ const t = readOpenCodeTokens(tokens);
89
+ if (t.input <= 0 && t.output <= 0) {
90
+ return null;
91
+ }
92
+ const cost = toNumber(info?.cost ?? 0);
93
+ return {
94
+ inputTokens: t.input,
95
+ cachedInputTokens: t.cacheRead,
96
+ outputTokens: t.output,
97
+ reasoningOutputTokens: t.reasoning,
98
+ cost,
99
+ rawUsageJson: {
100
+ tokens: { input: t.input, output: t.output, reasoning: t.reasoning, cache: { read: t.cacheRead, write: t.cacheWrite } },
101
+ cost,
102
+ },
103
+ };
104
+ }
17
105
  /**
18
106
  * Parses the `opencode run --format json` event stream (NDJSON). Extracts the
19
107
  * assistant transcript, provider-reported usage, native session id, and a
20
108
  * structured conversation including tool calls and reasoning, in stream order.
21
109
  *
22
- * OpenCode emits one event per line, each `{ type, part?, properties? }`.
23
- * Relevant part types: `text` (assistant), `reasoning`, and `tool` (a single
24
- * part that carries both the call input and, once finished, the output and
25
- * status under `part.state`). Usage is reported on `step_finish` events.
110
+ * OpenCode emits one event per line. The `run` command flattens each bus event
111
+ * to `{ type, part?, properties? }`. Relevant part types: `text` (assistant),
112
+ * `reasoning`, `tool` (a single part carrying both input and, once finished,
113
+ * output/status under `part.state`), and `step-finish` (per-LLM-call usage
114
+ * under `part.tokens`). Assistant messages (`properties.info`, role
115
+ * `assistant`) also carry a cumulative `tokens`/`cost`, used as a fallback when
116
+ * no `step-finish` parts are present.
26
117
  */
27
118
  export function parseOpenCodeJsonLines(stdout) {
28
119
  const textParts = [];
29
120
  const conversation = [];
30
- let inputTokens = 0;
31
- let outputTokens = 0;
32
121
  let nativeSessionId = null;
33
122
  let foundEvent = false;
123
+ // Usage summed across `step-finish` parts (one per completed LLM call).
124
+ const stepTotals = { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 };
125
+ let stepCost = 0;
126
+ let sawStepFinish = false;
127
+ // Fallback: latest cumulative usage per assistant message id.
128
+ const messageTotals = new Map();
34
129
  for (const line of stdout.split("\n")) {
35
130
  const trimmed = line.trim();
36
131
  if (!trimmed.startsWith("{")) {
@@ -41,14 +136,22 @@ export function parseOpenCodeJsonLines(stdout) {
41
136
  continue;
42
137
  }
43
138
  foundEvent = true;
44
- const part = asRecord(parsed.part);
45
139
  const properties = asRecord(parsed.properties);
46
- const info = asRecord(properties?.info);
47
- if (!nativeSessionId && typeof properties?.sessionID === "string") {
48
- nativeSessionId = properties.sessionID;
49
- }
50
- if (!nativeSessionId && typeof info?.id === "string") {
51
- nativeSessionId = info.id;
140
+ // The `run` formatter flattens parts to `parsed.part`; the raw bus/event
141
+ // shape nests them under `properties.part`. Accept either.
142
+ const part = asRecord(parsed.part) ?? asRecord(properties?.part);
143
+ const info = asRecord(properties?.info) ?? asRecord(parsed.info);
144
+ // Native session id (`ses_...`) appears on parts, messages, and event
145
+ // envelopes. The strict `ses_` regex lets us safely consider `info.id`
146
+ // (which is the session id on `session.created` but a `msg_` id on message
147
+ // events — the latter is simply rejected by the pattern).
148
+ if (!nativeSessionId) {
149
+ for (const candidate of [part?.sessionID, info?.sessionID, properties?.sessionID, parsed.sessionID, info?.id]) {
150
+ if (typeof candidate === "string" && SESSION_ID_RE.test(candidate)) {
151
+ nativeSessionId = candidate;
152
+ break;
153
+ }
154
+ }
52
155
  }
53
156
  const partType = typeof part?.type === "string" ? part.type : null;
54
157
  if (parsed.type === "text" && partType === "text" && typeof part?.text === "string" && part.text.trim()) {
@@ -101,22 +204,73 @@ export function parseOpenCodeJsonLines(stdout) {
101
204
  }
102
205
  continue;
103
206
  }
104
- if (parsed.type === "step_finish" && part) {
105
- const usage = asRecord(part.usage);
106
- if (usage) {
107
- inputTokens += toNumber(usage.promptTokens ?? usage.inputTokens ?? usage.input_tokens ?? 0);
108
- outputTokens += toNumber(usage.completionTokens ?? usage.outputTokens ?? usage.output_tokens ?? 0);
207
+ // Per-LLM-call usage. The step-finish marker can arrive as the part type or
208
+ // the flattened top-level type; the underscore spelling and a legacy
209
+ // `part.usage` object are tolerated for forward/backward compatibility.
210
+ const isStepFinish = partType === "step-finish" || partType === "step_finish"
211
+ || parsed.type === "step-finish" || parsed.type === "step_finish";
212
+ if (isStepFinish && part) {
213
+ const tokens = asRecord(part.tokens) ?? asRecord(part.usage);
214
+ if (tokens) {
215
+ const t = readOpenCodeTokens(tokens);
216
+ stepTotals.input += t.input;
217
+ stepTotals.output += t.output;
218
+ stepTotals.reasoning += t.reasoning;
219
+ stepTotals.cacheRead += t.cacheRead;
220
+ stepTotals.cacheWrite += t.cacheWrite;
221
+ stepCost += toNumber(part.cost ?? 0);
222
+ sawStepFinish = true;
223
+ }
224
+ continue;
225
+ }
226
+ // Assistant message carries cumulative usage for the message; message
227
+ // events stream repeatedly, so keep the latest value per message id.
228
+ if (info && info.role === "assistant") {
229
+ const tokens = asRecord(info.tokens);
230
+ if (tokens && typeof info.id === "string") {
231
+ messageTotals.set(info.id, { tokens: readOpenCodeTokens(tokens), cost: toNumber(info.cost ?? 0) });
109
232
  }
110
233
  }
111
234
  }
112
235
  if (!foundEvent) {
113
236
  return null;
114
237
  }
238
+ // Prefer per-step usage; fall back to the sum of final per-message usage.
239
+ let usage = stepTotals;
240
+ let cost = stepCost;
241
+ if (!sawStepFinish && messageTotals.size > 0) {
242
+ usage = { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 };
243
+ cost = 0;
244
+ for (const entry of messageTotals.values()) {
245
+ usage.input += entry.tokens.input;
246
+ usage.output += entry.tokens.output;
247
+ usage.reasoning += entry.tokens.reasoning;
248
+ usage.cacheRead += entry.tokens.cacheRead;
249
+ usage.cacheWrite += entry.tokens.cacheWrite;
250
+ cost += entry.cost;
251
+ }
252
+ }
253
+ const hasUsage = usage.input > 0 || usage.output > 0;
254
+ const rawUsageJson = hasUsage
255
+ ? {
256
+ tokens: {
257
+ input: usage.input,
258
+ output: usage.output,
259
+ reasoning: usage.reasoning,
260
+ cache: { read: usage.cacheRead, write: usage.cacheWrite },
261
+ },
262
+ cost,
263
+ }
264
+ : null;
115
265
  return {
116
266
  transcriptText: textParts.join("\n\n").trim(),
117
- inputTokens,
118
- outputTokens,
267
+ inputTokens: usage.input,
268
+ cachedInputTokens: usage.cacheRead,
269
+ outputTokens: usage.output,
270
+ reasoningOutputTokens: usage.reasoning,
271
+ cost,
119
272
  nativeSessionId,
273
+ rawUsageJson,
120
274
  conversation,
121
275
  };
122
276
  }
@@ -14,6 +14,7 @@ import { runProviderExecutionLoop } from "./provider-execution-loop.js";
14
14
  import { isTransientCodexTransportError, isClaudeConversationNotFoundError } from "../../../shared/providers/provider-error-classifier.js";
15
15
  import { CONTAINER_WORKSPACE_ROOT, CONTAINER_QWEN_OPENAI_LOG_DIR, resolveCodexOutputPath, cleanupCodexOutputPath, resolveQwenHostLogDir, resetQwenOpenAiLogDir, resolveAntigravityHostLogPath, resolveAntigravityContainerLogPath, cleanupProviderRuntimeArtifacts } from "./provider-runtime-artifacts.js";
16
16
  import { readQwenLogData, readCodexLatestSessionJson, readClaudeSessionJsonl, parseAntigravityConversationId, readAntigravityTranscript } from "./provider-transcripts.js";
17
+ import { parseOpenCodeJsonLines } from "./provider-logs/opencode-log-parser.js";
17
18
  import { collectProviderUsageTelemetry, } from "./provider-usage.js";
18
19
  export class ProviderRunner {
19
20
  dockerRunner;
@@ -252,6 +253,18 @@ export class ProviderRunner {
252
253
  if (provider === "antigravity" && !resolvedNativeSessionId && antigravityLogPath) {
253
254
  resolvedNativeSessionId = await parseAntigravityConversationId(cwd, antigravityLogPath, workflowSettings.executionMode, this.dockerRunner);
254
255
  }
256
+ // OpenCode reports no token usage on the `run --format json` stream; its
257
+ // session id is the only usage handle it gives us. Resolve it from the
258
+ // stream, then read authoritative usage via `opencode export`.
259
+ let opencodeExportJson = null;
260
+ if (provider === "opencode") {
261
+ if (!resolvedNativeSessionId) {
262
+ resolvedNativeSessionId = parseOpenCodeJsonLines(result.stdout)?.nativeSessionId ?? null;
263
+ }
264
+ if (resolvedNativeSessionId) {
265
+ opencodeExportJson = await this.readOpenCodeExport(cwd, resolvedNativeSessionId, providerEnv, sessionId, workflowSettings, repoPath, { providerMountAuth, providerAuthPath, mcpConnection: input.mcpConnection, customMcpServers: input.customMcpServers, signal });
266
+ }
267
+ }
255
268
  let antigravityTranscriptJsonl = null;
256
269
  if (provider === "antigravity" && resolvedNativeSessionId) {
257
270
  antigravityTranscriptJsonl = await readAntigravityTranscript(cwd, resolvedNativeSessionId, workflowSettings.executionMode, this.dockerRunner);
@@ -279,6 +292,7 @@ export class ProviderRunner {
279
292
  executionMode: workflowSettings.executionMode,
280
293
  antigravitySessionDbPath: tempDbPath,
281
294
  antigravityTranscriptJsonl,
295
+ opencodeExportJson,
282
296
  });
283
297
  return {
284
298
  ...result,
@@ -307,6 +321,43 @@ export class ProviderRunner {
307
321
  await cleanupProviderRuntimeArtifacts(provider, workflowSettings.executionMode, sessionId, cwd, antigravityLogPath, this.dockerRunner.removeWorkspaceDir ? this.dockerRunner.removeWorkspaceDir.bind(this.dockerRunner) : undefined);
308
322
  }
309
323
  }
324
+ /**
325
+ * Reads authoritative token usage for an OpenCode run by invoking
326
+ * `opencode export <sessionID>` against the same workspace (its `run
327
+ * --format json` stream carries no usage). In DOCKER mode this reuses the run
328
+ * volume via {@link IDockerRunner.runProviderInDocker}; in HOST mode it runs
329
+ * opencode directly. Returns the raw export stdout, or null on failure — the
330
+ * caller falls back to estimated usage.
331
+ */
332
+ async readOpenCodeExport(cwd, nativeSessionId, providerEnv, sessionId, workflowSettings, repoPath, opts) {
333
+ try {
334
+ if (workflowSettings.executionMode === "DOCKER") {
335
+ const result = await this.dockerRunner.runProviderInDocker({
336
+ command: "opencode",
337
+ args: ["export", nativeSessionId],
338
+ cwd,
339
+ providerEnv,
340
+ // Distinct container name from the run so the two never collide.
341
+ sessionId: `${sessionId}-export`,
342
+ providerLabel: "opencode",
343
+ workflowSettings,
344
+ repoPath,
345
+ signal: opts.signal,
346
+ onActivity: () => undefined,
347
+ providerMountAuth: opts.providerMountAuth,
348
+ providerAuthPath: opts.providerAuthPath,
349
+ mcpConnection: opts.mcpConnection,
350
+ customMcpServers: opts.customMcpServers,
351
+ });
352
+ return result.stdout || null;
353
+ }
354
+ const result = await runStreamingCommand("opencode", ["export", nativeSessionId], cwd, providerEnv, { signal: opts.signal });
355
+ return result.stdout || null;
356
+ }
357
+ catch {
358
+ return null;
359
+ }
360
+ }
310
361
  async writeLocalOpenCodeConfig(content, repoPath, sessionId) {
311
362
  if (!content) {
312
363
  return null;
@@ -4,7 +4,7 @@ import * as path from "path";
4
4
  import { countTokens as countAnthropicTokens } from "@anthropic-ai/tokenizer";
5
5
  import { encodingForModel } from "js-tiktoken";
6
6
  import { parseCodexRolloutJsonl, parseCodexExecStdout } from "./provider-logs/codex-log-parser.js";
7
- import { parseOpenCodeJsonLines } from "./provider-logs/opencode-log-parser.js";
7
+ import { parseOpenCodeJsonLines, parseOpenCodeExport } from "./provider-logs/opencode-log-parser.js";
8
8
  import { buildQwenConversation, parseQwenOpenAiLogs, readQwenOpenAiLogRecords, sumQwenOpenAiUsage, extractQwenUsageRecord, } from "./provider-logs/qwen-log-parser.js";
9
9
  import { parseClaudeCodeSessionJsonl, } from "./provider-logs/claude-code-log-parser.js";
10
10
  import { parseAntigravityDatabase, parseAntigravityTranscript, } from "./provider-logs/antigravity-log-parser.js";
@@ -302,17 +302,35 @@ export async function collectProviderUsageTelemetry(args) {
302
302
  }
303
303
  if (args.provider === "opencode") {
304
304
  const parsed = parseOpenCodeJsonLines(args.stdout);
305
+ // The `run --format json` stream carries the transcript, conversation, and
306
+ // session id but no token usage. Authoritative usage comes from
307
+ // `opencode export <sessionID>` (info.tokens), captured post-run.
308
+ const exportUsage = args.opencodeExportJson ? parseOpenCodeExport(args.opencodeExportJson) : null;
305
309
  if (parsed) {
306
310
  const transcriptText = parsed.transcriptText || fallbackOutput;
307
311
  const conversation = withLeadingUserTurn(parsed.conversation, args.prompt);
308
- if (parsed.inputTokens > 0 || parsed.outputTokens > 0) {
312
+ // Prefer exported session usage, then any usage the stream happened to
313
+ // carry (older opencode builds), then estimation.
314
+ const reported = exportUsage
315
+ ?? ((parsed.inputTokens > 0 || parsed.outputTokens > 0)
316
+ ? {
317
+ inputTokens: parsed.inputTokens,
318
+ cachedInputTokens: parsed.cachedInputTokens,
319
+ outputTokens: parsed.outputTokens,
320
+ reasoningOutputTokens: parsed.reasoningOutputTokens,
321
+ rawUsageJson: parsed.rawUsageJson,
322
+ }
323
+ : null);
324
+ if (reported) {
309
325
  return {
310
326
  ...emptyTelemetry(),
311
- inputTokens: parsed.inputTokens,
312
- outputTokens: parsed.outputTokens,
313
- totalTokens: parsed.inputTokens + parsed.outputTokens,
327
+ inputTokens: reported.inputTokens,
328
+ cachedInputTokens: reported.cachedInputTokens,
329
+ outputTokens: reported.outputTokens,
330
+ reasoningOutputTokens: reported.reasoningOutputTokens,
331
+ totalTokens: reported.inputTokens + reported.outputTokens,
314
332
  usageSource: "reported",
315
- rawUsageJson: null,
333
+ rawUsageJson: reported.rawUsageJson,
316
334
  transcriptText,
317
335
  nativeSessionId: parsed.nativeSessionId,
318
336
  conversation,
@@ -323,6 +341,21 @@ export async function collectProviderUsageTelemetry(args) {
323
341
  estimated.conversation = conversation;
324
342
  return estimated;
325
343
  }
344
+ if (exportUsage) {
345
+ return {
346
+ ...emptyTelemetry(),
347
+ inputTokens: exportUsage.inputTokens,
348
+ cachedInputTokens: exportUsage.cachedInputTokens,
349
+ outputTokens: exportUsage.outputTokens,
350
+ reasoningOutputTokens: exportUsage.reasoningOutputTokens,
351
+ totalTokens: exportUsage.inputTokens + exportUsage.outputTokens,
352
+ usageSource: "reported",
353
+ rawUsageJson: exportUsage.rawUsageJson,
354
+ transcriptText: fallbackOutput,
355
+ nativeSessionId: args.nativeSessionId || null,
356
+ conversation: [],
357
+ };
358
+ }
326
359
  return estimateTelemetry("opencode", args.model, args.prompt, fallbackOutput);
327
360
  }
328
361
  if (args.provider === "antigravity") {
@@ -35,22 +35,32 @@ export class WorkspaceArtifactService {
35
35
  this.workspaceManager = workspaceManager;
36
36
  }
37
37
  async exportBinaryPatch(workspaceRef, baseRef) {
38
- const diffArgs = [
39
- "diff",
40
- "--binary",
41
- baseRef,
42
- "--",
43
- ".",
38
+ // Pathspecs shared by the diff and the untracked-file scan. Keeping them in
39
+ // sync matters: `.code-ux-home` holds each provider's sprint HOME, and some
40
+ // CLIs (notably opencode) keep a churning git snapshot store there with tens
41
+ // of thousands of ephemeral loose objects. If `ls-files` is allowed to walk
42
+ // it, the output can exceed the command runner's stdout clip limit, which
43
+ // truncates the head of the stream and corrupts the first path — that
44
+ // corrupted path then slips past the JS filter and makes `git add
45
+ // --intent-to-add` fail with "pathspec did not match any files", taking the
46
+ // whole export (and the task) down with it. Excluding it at the source keeps
47
+ // the listing small and correct.
48
+ const excludePathspecs = [
44
49
  `:(exclude)${LEARNINGS_FILENAME}`,
45
50
  ":(exclude).code-ux-home",
46
51
  ":(exclude).code-ux-home/**",
47
52
  ":(exclude,glob)**/logs/openai/**",
48
53
  ":(exclude,glob)logs/openai/**",
49
54
  ];
50
- const untrackedResult = await this.workspaceManager.runWorkspaceCommand(workspaceRef, "git", ["ls-files", "--others", "--exclude-standard", "-z"], { trimOutput: false });
55
+ const diffArgs = ["diff", "--binary", baseRef, "--", ".", ...excludePathspecs];
56
+ const untrackedResult = await this.workspaceManager.runWorkspaceCommand(workspaceRef, "git", ["ls-files", "--others", "--exclude-standard", "-z", "--", ".", ...excludePathspecs], { trimOutput: false });
51
57
  const untrackedPaths = untrackedResult.stdout
52
58
  .split("\0")
53
59
  .filter((candidate) => (candidate.length > 0
60
+ // Defense in depth: a clipped stdout stream is prefixed with "..." and
61
+ // begins mid-path. Such a fragment is never a real ls-files entry, so
62
+ // drop it rather than feed a bogus pathspec to `git add`.
63
+ && !candidate.startsWith("...")
54
64
  && candidate !== LEARNINGS_FILENAME
55
65
  && !isProtectedExportPath(candidate)
56
66
  && !isQwenOpenAiLogPath(candidate)));
@@ -641,6 +641,38 @@ export class ExecutionRepository {
641
641
  WHERE id = ?
642
642
  `).run(trimmed, nativeSessionId ?? trimmed, now, invocationId);
643
643
  }
644
+ associateProviderInvocationRuntime(invocationId, input) {
645
+ try {
646
+ const current = requireProviderInvocationUsage((id) => this.getProviderInvocationUsage(id), invocationId);
647
+ const sprintRunId = input.sprintRunId === undefined ? current.sprintRunId : input.sprintRunId;
648
+ const dispatchId = input.dispatchId === undefined ? current.dispatchId : input.dispatchId;
649
+ const taskRunId = input.taskRunId === undefined ? current.taskRunId : input.taskRunId;
650
+ if (sprintRunId) {
651
+ requireSprintRun((id) => this.getSprintRun(id), sprintRunId);
652
+ }
653
+ if (dispatchId) {
654
+ requireTaskDispatch((id) => this.getTaskDispatch(id), dispatchId);
655
+ }
656
+ if (taskRunId) {
657
+ requireTaskRun((id) => this.getTaskRun(id), taskRunId);
658
+ }
659
+ const now = new Date().toISOString();
660
+ this.db.prepare(`
661
+ UPDATE provider_invocations
662
+ SET sprint_run_id = ?, dispatch_id = ?, task_run_id = ?, updated_at = ?
663
+ WHERE id = ?
664
+ `).run(sprintRunId ?? null, dispatchId ?? null, taskRunId ?? null, now, invocationId);
665
+ const updated = requireProviderInvocationUsage((id) => this.getProviderInvocationUsage(id), invocationId);
666
+ this.notifyRealtime(updated.projectId, false);
667
+ return updated;
668
+ }
669
+ catch (error) {
670
+ if (error instanceof RepositoryError)
671
+ throw error;
672
+ this.logger.error("Operation failed", { error, invocationId });
673
+ throw new RepositoryError(error instanceof Error ? error.message : "Operation failed", error);
674
+ }
675
+ }
644
676
  updateProviderInvocationUsage(invocationId, input) {
645
677
  try {
646
678
  const current = requireProviderInvocationUsage((id) => this.getProviderInvocationUsage(id), invocationId);
@@ -868,6 +900,51 @@ export class ExecutionRepository {
868
900
  throw new RepositoryError(error instanceof Error ? error.message : "Operation failed", error);
869
901
  }
870
902
  }
903
+ reassignTaskRunSprintRun(taskRunId, sprintRunId) {
904
+ try {
905
+ const current = requireTaskRun((id) => this.getTaskRun(id), taskRunId);
906
+ requireSprintRunScoped((id) => this.getSprintRun(id), sprintRunId, current.projectId, current.sprintId);
907
+ this.db.prepare(`
908
+ UPDATE task_runs
909
+ SET sprint_run_id = ?
910
+ WHERE id = ?
911
+ `).run(sprintRunId, taskRunId);
912
+ const updated = requireTaskRun((id) => this.getTaskRun(id), taskRunId);
913
+ if (current.sprintRunId) {
914
+ this.wallTimeQuery.invalidateSprintRun(current.projectId, current.sprintRunId);
915
+ }
916
+ this.wallTimeQuery.invalidateSprintRun(updated.projectId, sprintRunId);
917
+ this.notifyRealtime(updated.projectId, false);
918
+ return updated;
919
+ }
920
+ catch (error) {
921
+ if (error instanceof RepositoryError)
922
+ throw error;
923
+ this.logger.error("Operation failed", { error, taskRunId, sprintRunId });
924
+ throw new RepositoryError(error instanceof Error ? error.message : "Operation failed", error);
925
+ }
926
+ }
927
+ reassignTaskDispatchSprintRun(dispatchId, sprintRunId) {
928
+ try {
929
+ const current = requireTaskDispatch((id) => this.getTaskDispatch(id), dispatchId);
930
+ requireSprintRunScoped((id) => this.getSprintRun(id), sprintRunId, current.projectId, current.sprintId);
931
+ const now = new Date().toISOString();
932
+ this.db.prepare(`
933
+ UPDATE task_dispatches
934
+ SET sprint_run_id = ?, updated_at = ?
935
+ WHERE id = ?
936
+ `).run(sprintRunId, now, dispatchId);
937
+ const updated = requireTaskDispatch((id) => this.getTaskDispatch(id), dispatchId);
938
+ this.notifyRealtime(updated.projectId, true);
939
+ return updated;
940
+ }
941
+ catch (error) {
942
+ if (error instanceof RepositoryError)
943
+ throw error;
944
+ this.logger.error("Operation failed", { error, dispatchId, sprintRunId });
945
+ throw new RepositoryError(error instanceof Error ? error.message : "Operation failed", error);
946
+ }
947
+ }
871
948
  listLatestTaskRuns(taskIds, sprintRunId) {
872
949
  const uniqueTaskIds = [...new Set(taskIds.map((taskId) => taskId.trim()).filter(Boolean))];
873
950
  if (uniqueTaskIds.length === 0) {
@@ -12,6 +12,14 @@ function countConversationToolCalls(conversation) {
12
12
  }
13
13
  return conversation.reduce((count, turn) => (turn.kind === "tool_call" ? count + 1 : count), 0);
14
14
  }
15
+ function isRestartInterruptedDockerInvocation(error, args) {
16
+ if (args.workflowSettings.executionMode !== "DOCKER") {
17
+ return false;
18
+ }
19
+ const message = error instanceof Error ? error.message : String(error || "");
20
+ return (/Command spawner host exited/i.test(message)
21
+ && /(signal=SIGINT|signal=SIGTERM|signal=SIGHUP)/i.test(message));
22
+ }
15
23
  /** Resolves the effective model name to use for telemetry and recording. */
16
24
  export function resolveEffectiveModel(args) {
17
25
  const { provider, model, customModel } = args;
@@ -227,7 +235,8 @@ export class ProviderExecutionService {
227
235
  : await this.deps.providerRunner.runProvider(runnerOpts);
228
236
  }
229
237
  catch (error) {
230
- if (invocation && this.deps.executionRepository) {
238
+ const preserveForStartupRecovery = isRestartInterruptedDockerInvocation(error, args);
239
+ if (invocation && this.deps.executionRepository && !preserveForStartupRecovery) {
231
240
  const finishedAt = new Date().toISOString();
232
241
  const durationMs = Date.now() - startedMs;
233
242
  this.deps.executionRepository.updateProviderInvocationUsage(invocation.id, {