@codeuxai/codeux 0.8.2 → 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.
- package/README.md +128 -45
- package/dashboard/dist/assets/{AgentAvatarScene-3Fa3RQHc.js → AgentAvatarScene-CdYNjBsU.js} +1 -1
- package/dashboard/dist/assets/{AgentsPage-woorcNHq.js → AgentsPage-BKJ1Zdbv.js} +2 -2
- package/dashboard/dist/assets/{BackgroundManager-DbgGcx7x.js → BackgroundManager-B4A4jACF.js} +1 -1
- package/dashboard/dist/assets/{BranchNameSchemeEditor-Bk2p-g2z.js → BranchNameSchemeEditor-DfnH2VYW.js} +1 -1
- package/dashboard/dist/assets/{BrowserPage-BLTJo_tK.js → BrowserPage-BxfAsG3M.js} +1 -1
- package/dashboard/dist/assets/{ChatPage-BljTU3OV.js → ChatPage-MXugCDvS.js} +1 -1
- package/dashboard/dist/assets/{ErrorPage-DgYd7CGv.js → ErrorPage-Y-fEDzly.js} +1 -1
- package/dashboard/dist/assets/{FileBrowserPage-ChIelEM6.js → FileBrowserPage-B4JEdW9B.js} +1 -1
- package/dashboard/dist/assets/{KnowledgePage-DP7v0vk3.js → KnowledgePage-DVbW7o61.js} +1 -1
- package/dashboard/dist/assets/{ListWindowSelector-DtKcmXGY.js → ListWindowSelector-CPMgebqM.js} +1 -1
- package/dashboard/dist/assets/{MemoryPage-2N_EfqqO.js → MemoryPage-C3RWZJL5.js} +1 -1
- package/dashboard/dist/assets/{OverviewTelemetry-C1hoOMVQ.js → OverviewTelemetry-Cy9vzhdY.js} +1 -1
- package/dashboard/dist/assets/{ProjectsPage-CEn6QDXI.js → ProjectsPage-RmR2LMuR.js} +1 -1
- package/dashboard/dist/assets/{SchedulerPage-BDTm8Q3w.js → SchedulerPage-Bg08X7Tm.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-CvSsreD8.js → SettingsPage-DzE_F7Kw.js} +1 -1
- package/dashboard/dist/assets/{SprintBoatRace-Ryphd2ZR.js → SprintBoatRace-BeZOItMO.js} +1 -1
- package/dashboard/dist/assets/{SprintDag-BLP6gb8k.js → SprintDag-vF8f33gn.js} +1 -1
- package/dashboard/dist/assets/{SprintsPage-DORSqECT.js → SprintsPage-bRHQPfWP.js} +1 -1
- package/dashboard/dist/assets/{StatsPage-BODQk1cJ.js → StatsPage-BZl_tzUr.js} +1 -1
- package/dashboard/dist/assets/{TasksPage-PeouQ4EB.js → TasksPage-rLMlkgxX.js} +1 -1
- package/dashboard/dist/assets/{index-DddY8OV0.js → index-DdNzHP9F.js} +5 -5
- package/dashboard/dist/assets/{monaco.contribution-Bxle9Jvf.js → monaco.contribution-2KFAi0p0.js} +2 -2
- package/dashboard/dist/assets/{tsMode-BYb4cdGe.js → tsMode-dZI5bz0O.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/infrastructure/providers/cli/provider-logs/opencode-log-parser.js +174 -20
- package/dist/infrastructure/providers/cli/provider-runner.js +51 -0
- package/dist/infrastructure/providers/cli/provider-usage.js +39 -6
- package/dist/infrastructure/providers/cli/workspace-artifact-service.js +17 -7
- package/dist/repositories/execution-repository.js +77 -0
- package/dist/services/provider-execution-service.js +10 -1
- package/dist/services/runtime-startup-recovery-service.js +170 -0
- package/dist/services/sprint-task-dispatch-service.js +7 -0
- package/docs/getting-started/quickstart.md +14 -3
- package/docs/operations/runbook.md +2 -0
- package/docs/settings/configuration-and-storage.md +2 -0
- package/package.json +1 -1
package/dashboard/dist/assets/{monaco.contribution-Bxle9Jvf.js → monaco.contribution-2KFAi0p0.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/tsMode-
|
|
2
|
-
import{r as e}from"./rolldown-runtime-Cyuzqnbw.js";import{Ii as t}from"./index-
|
|
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-
|
|
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-
|
|
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
|
|
23
|
-
* Relevant part types: `text` (assistant),
|
|
24
|
-
*
|
|
25
|
-
* status under `part.state`)
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
312
|
-
|
|
313
|
-
|
|
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:
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
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, {
|