@straiffi/archon 1.11.4 → 1.11.6
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 +8 -0
- package/dist/client/assets/{ActivityView-Cj-JRM9h.js → ActivityView-D9dXuvls.js} +1 -1
- package/dist/client/assets/{GitHubActionsDialog-Bz7x-EXk.js → GitHubActionsDialog-DFB9FNX0.js} +1 -1
- package/dist/client/assets/{VerificationDialog-C1g5teUk.js → VerificationDialog-CCpZtj3u.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-Czgp4_s_.js → architectureDiagram-3BPJPVTR-C5QNVdbR.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-D_eKxSQA.js → blockDiagram-GPEHLZMM-ygrKiT2B.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-D6nMuDM5.js → c4Diagram-AAUBKEIU-p6pvarzH.js} +1 -1
- package/dist/client/assets/channel-CjCFIS-2.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-B6Mn8gmT.js → chunk-2J33WTMH-BEZXyYSy.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-BBoS7imT.js → chunk-3OPIFGDE-CxmfjZmF.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-CdhOo0Gr.js → chunk-5ZQYHXKU-B2X60oxE.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Dq8Odwy5.js → chunk-727SXJPM-P03pDtRv.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DsKFdfMh.js → chunk-AQP2D5EJ-DE0MLi-X.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-4Ol5HlMd.js → chunk-CSCIHK7Q-C3QVx-Wf.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-Ao7uFEEm.js → chunk-KSCS5N6A-BvajGF5R.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-C9Gzx6GF.js → chunk-L5ZTLDWV-HtpQP1op.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-Ceca_Ske.js → chunk-LZXEDZCA-CWfF3VMY.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-Cs2auQCc.js → chunk-ND2GUHAM-kEAduwJl.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-fssKuUqc.js → chunk-NZK2D7GU-DNG4qBtm.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BdRwmtR1.js → chunk-O5CBEL6O-CS4J6DbD.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-DnWsSIYg.js → chunk-WU5MYG2G-B2-mpLQ2.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DQ1lcUTi.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-tiWbaJIg.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-B71wyESx.js → dagre-BM42HDAG-CNK_Lkn1.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D7Xht5lG.js → diagram-2AECGRRQ-BGKodAdj.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-BIL2jzaH.js → diagram-5GNKFQAL-O7IcwCBh.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CKLHtkTQ.js → diagram-KO2AKTUF-BzuzpIm4.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CoTDrm1f.js → diagram-LMA3HP47-C6xFuQYj.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-a6qcioel.js → diagram-OG6HWLK6-hp2uvo99.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-CUTe5tSP.js → erDiagram-TEJ5UH35-CiiZ8B7W.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-CSDL4PU-.js → flowDiagram-I6XJVG4X-CMXv6xN7.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-Ck4JCO9N.js → ganttDiagram-6RSMTGT7-EP8OVqmy.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-D8h23S3l.js → gitGraphDiagram-PVQCEYII-Dacq4hgw.js} +1 -1
- package/dist/client/assets/index-a8IeMnRR.css +2 -0
- package/dist/client/assets/index-uGt4qt5t.js +287 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Bq1wj1vH.js → infoDiagram-5YYISTIA-DpTIfB1a.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C6ry_1gD.js → ishikawaDiagram-YF4QCWOH-DOcryUZC.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BCoSw_Ye.js → journeyDiagram-JHISSGLW-DgEfwhed.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-CvHdWXxq.js → kanban-definition-UN3LZRKU-Bg9Kz1IT.js} +1 -1
- package/dist/client/assets/{line-DDPHAHbi.js → line-DGt3zplT.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-B7v3eJbR.js → mermaid-parser.core-CeZgcMLs.js} +1 -1
- package/dist/client/assets/{mermaid.core-DoJjZ2zS.js → mermaid.core-Bp6d14EP.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-D8uwnlrG.js → mindmap-definition-RKZ34NQL-CyJdo6SE.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-B2zLClZg.js → pieDiagram-4H26LBE5-elm12Uf5.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DGGYHTHx.js → quadrantDiagram-W4KKPZXB-BXuIBixh.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-JfCBqWur.js → requirementDiagram-4Y6WPE33-C5rIaCf4.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D9nUF-4P.js → sankeyDiagram-5OEKKPKP-CIYPcDTl.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DGnNkDzT.js → sequenceDiagram-3UESZ5HK-CX_go9-1.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CFZlnYcA.js → stateDiagram-AJRCARHV-CYbsPFSQ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-QZ_UdR0J.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-C-KYYKMX.js → timeline-definition-PNZ67QCA-B3pBx_YB.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-C3MGIjT1.js → vennDiagram-CIIHVFJN-B1Z6hLI-.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CUD7E4c3.js → wardleyDiagram-YWT4CUSO-BvJEPN2P.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CM2Q4-TE.js → xychartDiagram-2RQKCTM6-CKavkWIt.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +63 -4
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +55 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +277 -299
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/agentOutputLanguage.js +11 -0
- package/dist/server/lib/agentOutputLanguage.js.map +1 -0
- package/dist/server/lib/agentProvider.js.map +1 -1
- package/dist/server/lib/agentRuns.js +68 -4
- package/dist/server/lib/agentRuns.js.map +1 -1
- package/dist/server/lib/appLocale.js +19 -0
- package/dist/server/lib/appLocale.js.map +1 -0
- package/dist/server/lib/buildExecutionEnvironment.js +92 -1
- package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/chatOperations.js +10 -3
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/conversationResume.js +56 -3
- package/dist/server/lib/conversationResume.js.map +1 -1
- package/dist/server/lib/dockerSandboxOpencodeSessionOperations.js +81 -25
- package/dist/server/lib/dockerSandboxOpencodeSessionOperations.js.map +1 -1
- package/dist/server/lib/dockerSandboxReleaseProfiles.js +36 -7
- package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -1
- package/dist/server/lib/executionEnvironmentRoutes.js +71 -4
- package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidation.js +199 -19
- package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidationEvidence.js +140 -0
- package/dist/server/lib/executionEnvironmentValidationEvidence.js.map +1 -0
- package/dist/server/lib/executionEnvironmentValidationOperations.js +13 -0
- package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
- package/dist/server/lib/executionRetryContext.js +13 -0
- package/dist/server/lib/executionRetryContext.js.map +1 -1
- package/dist/server/lib/figmaResources.js +9 -2
- package/dist/server/lib/figmaResources.js.map +1 -1
- package/dist/server/lib/hostExecutionEnvironment.js +14 -2
- package/dist/server/lib/hostExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/i18n/index.js +3 -2
- package/dist/server/lib/i18n/index.js.map +1 -1
- package/dist/server/lib/i18n/resources.js +0 -1
- package/dist/server/lib/i18n/resources.js.map +1 -1
- package/dist/server/lib/lightweightPrompt.js +4 -0
- package/dist/server/lib/lightweightPrompt.js.map +1 -1
- package/dist/server/lib/models.js +58 -18
- package/dist/server/lib/models.js.map +1 -1
- package/dist/server/lib/opencodeAgentProvider.js +54 -48
- package/dist/server/lib/opencodeAgentProvider.js.map +1 -1
- package/dist/server/lib/opencodeCli.js +244 -0
- package/dist/server/lib/opencodeCli.js.map +1 -0
- package/dist/server/lib/opencodePromptOutput.js +11 -65
- package/dist/server/lib/opencodePromptOutput.js.map +1 -1
- package/dist/server/lib/opencodeSessionOperations.js +44 -28
- package/dist/server/lib/opencodeSessionOperations.js.map +1 -1
- package/dist/server/lib/opencodeSessionProvenance.js +23 -0
- package/dist/server/lib/opencodeSessionProvenance.js.map +1 -0
- package/dist/server/lib/opencodeStream.js +263 -0
- package/dist/server/lib/opencodeStream.js.map +1 -0
- package/dist/server/lib/opencodeTaskSessions.js +3 -4
- package/dist/server/lib/opencodeTaskSessions.js.map +1 -1
- package/dist/server/lib/opencodeV2SessionOperations.js +453 -0
- package/dist/server/lib/opencodeV2SessionOperations.js.map +1 -0
- package/dist/server/lib/run.js +27 -41
- package/dist/server/lib/run.js.map +1 -1
- package/dist/server/lib/runProcessIdentity.js +10 -2
- package/dist/server/lib/runProcessIdentity.js.map +1 -1
- package/dist/server/lib/runProcesses.js +52 -19
- package/dist/server/lib/runProcesses.js.map +1 -1
- package/dist/server/lib/runServiceStop.js +62 -0
- package/dist/server/lib/runServiceStop.js.map +1 -0
- package/dist/server/lib/runServices.js +63 -4
- package/dist/server/lib/runServices.js.map +1 -1
- package/dist/server/lib/runSessionLifecycle.js +25 -0
- package/dist/server/lib/runSessionLifecycle.js.map +1 -1
- package/dist/server/lib/runtimePaths.js +7 -4
- package/dist/server/lib/runtimePaths.js.map +1 -1
- package/dist/server/lib/serviceRunLogs.js +179 -0
- package/dist/server/lib/serviceRunLogs.js.map +1 -0
- package/dist/server/lib/specOperations.js +7 -2
- package/dist/server/lib/specOperations.js.map +1 -1
- package/dist/server/lib/specReviewContext.js +3 -2
- package/dist/server/lib/specReviewContext.js.map +1 -1
- package/dist/server/lib/toolUpgrade.js +54 -5
- package/dist/server/lib/toolUpgrade.js.map +1 -1
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/workers/build.js +47 -9
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +74 -10
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +59 -22
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +109 -169
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +27 -7
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +43 -10
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specReview.js +1 -0
- package/dist/server/workers/specReview.js.map +1 -1
- package/dist/server/workers/specSession.js +42 -5
- package/dist/server/workers/specSession.js.map +1 -1
- package/dist/shared/i18n/locales.js +12 -0
- package/dist/shared/i18n/locales.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/channel-BsoUrvI0.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-D79OdDcf.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CyG7JUxO.js +0 -1
- package/dist/client/assets/index-Ckj_syqH.js +0 -287
- package/dist/client/assets/index-RM0FaS4q.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BB3Eu4sn.js +0 -1
package/dist/server/lib/agent.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getChatSession, updateChatSessionUsageSnapshot } from './chats.js';
|
|
|
6
6
|
import { normalizeAgentModelId, requireExecutableAgent } from './agentCatalog.js';
|
|
7
7
|
import { createOpencodeTaskSessionReader, parseOpencodeTaskEnvelope } from './opencodeTaskSessions.js';
|
|
8
8
|
import { hostOpencodeSessionOperations } from './opencodeSessionOperations.js';
|
|
9
|
+
import { getOpencodeCliCompatibility, inspectOpencodeRelease, requireSupportedOpencodeCompatibility } from './opencodeCli.js';
|
|
9
10
|
import { hostExecutionEnvironment } from './hostExecutionEnvironment.js';
|
|
10
11
|
import { markRetryableOpenCodeDatabaseLock, RetryableOpenCodeDatabaseLockError, retryOpenCodeDatabaseLocks } from './opencodeExecutionReliability.js';
|
|
11
12
|
import { claudeAgentProvider } from './claudeAgentProvider.js';
|
|
@@ -22,7 +23,7 @@ import { getTicket } from './tickets.js';
|
|
|
22
23
|
import { createTranscriptRunSnapshots } from './transcriptRuns.js';
|
|
23
24
|
import { createHostLauncherEnvironment, createLegacyHostRuntimeEnvironment, createWorkspaceProcessEnvironment, } from './workspaceProcessEnvironment.js';
|
|
24
25
|
import { OpencodeModelResolutionError, parseOpencodeStderrModelResolutionError, } from './opencodeErrors.js';
|
|
25
|
-
import { extractLeadingOpencodeJsonObjects } from './
|
|
26
|
+
import { decodeOpencodeStreamEvent, extractLeadingOpencodeJsonObjects, extractOpencodeStreamUsage, parseOpencodeStreamLine } from './opencodeStream.js';
|
|
26
27
|
import { assertAgentSpawnPermit, AgentSpawnPermitRevokedError } from './agentSpawnPermits.js';
|
|
27
28
|
import { buildClaudeStdinMessage } from './claudeStdin.js';
|
|
28
29
|
import { enrichPromptWithPreview } from './agentPreview.js';
|
|
@@ -67,6 +68,23 @@ const getStreamingAgentProvider = (tool) => {
|
|
|
67
68
|
}
|
|
68
69
|
return opencodeAgentProvider;
|
|
69
70
|
};
|
|
71
|
+
const resolveOpencodeCompatibility = (tool, executionProvider, runtimeIdentity) => {
|
|
72
|
+
if (tool !== 'opencode') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (executionProvider.kind !== 'docker_sandbox') {
|
|
76
|
+
const compatibility = requireSupportedOpencodeCompatibility(getOpencodeCliCompatibility({ force: true }));
|
|
77
|
+
if (runtimeIdentity && (compatibility.runtimeIdentity.version !== runtimeIdentity.version
|
|
78
|
+
|| compatibility.runtimeIdentity.major !== runtimeIdentity.major)) {
|
|
79
|
+
throw new Error('The selected host OpenCode runtime no longer matches its resolved compatibility evidence.');
|
|
80
|
+
}
|
|
81
|
+
return compatibility;
|
|
82
|
+
}
|
|
83
|
+
if (!runtimeIdentity) {
|
|
84
|
+
throw new Error('The selected OpenCode execution environment has no validated runtime compatibility.');
|
|
85
|
+
}
|
|
86
|
+
return requireSupportedOpencodeCompatibility(inspectOpencodeRelease(runtimeIdentity.version));
|
|
87
|
+
};
|
|
70
88
|
const getAgentStderr = (tool, chunks) => {
|
|
71
89
|
const stderr = Buffer.concat(chunks).toString();
|
|
72
90
|
return tool === 'codex' ? redactCodexDiagnostic(stderr) : stderr;
|
|
@@ -113,30 +131,6 @@ export const EMPTY_CHAT_USAGE_SNAPSHOT = {
|
|
|
113
131
|
usage_snapshot_reasoning_tokens: null,
|
|
114
132
|
usage_snapshot_cost: null,
|
|
115
133
|
};
|
|
116
|
-
const toFiniteNumber = (value) => {
|
|
117
|
-
return typeof value === 'number' && Number.isFinite(value)
|
|
118
|
-
? value
|
|
119
|
-
: null;
|
|
120
|
-
};
|
|
121
|
-
const getValueAtPath = (value, path) => {
|
|
122
|
-
let current = value;
|
|
123
|
-
for (const segment of path) {
|
|
124
|
-
if (!current || typeof current !== 'object' || !(segment in current)) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
current = current[segment];
|
|
128
|
-
}
|
|
129
|
-
return current;
|
|
130
|
-
};
|
|
131
|
-
const getNumberAtPaths = (value, paths) => {
|
|
132
|
-
for (const path of paths) {
|
|
133
|
-
const candidate = toFiniteNumber(getValueAtPath(value, path));
|
|
134
|
-
if (candidate !== null) {
|
|
135
|
-
return candidate;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
};
|
|
140
134
|
const mergeChatUsageSnapshot = (current, next) => {
|
|
141
135
|
return {
|
|
142
136
|
usage_snapshot_context_window: next.usage_snapshot_context_window ?? current.usage_snapshot_context_window,
|
|
@@ -149,105 +143,8 @@ const mergeChatUsageSnapshot = (current, next) => {
|
|
|
149
143
|
usage_snapshot_cost: next.usage_snapshot_cost ?? current.usage_snapshot_cost,
|
|
150
144
|
};
|
|
151
145
|
};
|
|
152
|
-
const
|
|
153
|
-
if (!value || typeof value !== 'object') {
|
|
154
|
-
return {};
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
usage_snapshot_context_window: getNumberAtPaths(value, [
|
|
158
|
-
['contextWindow'],
|
|
159
|
-
['context_window'],
|
|
160
|
-
['result', 'contextWindow'],
|
|
161
|
-
['result', 'context_window'],
|
|
162
|
-
['limit', 'context'],
|
|
163
|
-
['limits', 'context'],
|
|
164
|
-
]),
|
|
165
|
-
usage_snapshot_max_output_tokens: getNumberAtPaths(value, [
|
|
166
|
-
['maxOutputTokens'],
|
|
167
|
-
['max_output_tokens'],
|
|
168
|
-
['result', 'maxOutputTokens'],
|
|
169
|
-
['result', 'max_output_tokens'],
|
|
170
|
-
['limit', 'output'],
|
|
171
|
-
['limits', 'output'],
|
|
172
|
-
]),
|
|
173
|
-
usage_snapshot_total_tokens: getNumberAtPaths(value, [
|
|
174
|
-
['tokens', 'total'],
|
|
175
|
-
['usage', 'tokens', 'total'],
|
|
176
|
-
['usage', 'total'],
|
|
177
|
-
['usage', 'totalTokens'],
|
|
178
|
-
['result', 'tokens', 'total'],
|
|
179
|
-
['result', 'usage', 'tokens', 'total'],
|
|
180
|
-
['result', 'usage', 'total'],
|
|
181
|
-
['result', 'usage', 'totalTokens'],
|
|
182
|
-
]),
|
|
183
|
-
usage_snapshot_input_tokens: getNumberAtPaths(value, [
|
|
184
|
-
['message', 'usage', 'input_tokens'],
|
|
185
|
-
['usage', 'input_tokens'],
|
|
186
|
-
['tokens', 'input'],
|
|
187
|
-
['usage', 'tokens', 'input'],
|
|
188
|
-
['usage', 'input'],
|
|
189
|
-
['usage', 'inputTokens'],
|
|
190
|
-
['result', 'tokens', 'input'],
|
|
191
|
-
['result', 'usage', 'tokens', 'input'],
|
|
192
|
-
['result', 'usage', 'input'],
|
|
193
|
-
['result', 'usage', 'inputTokens'],
|
|
194
|
-
]),
|
|
195
|
-
usage_snapshot_output_tokens: getNumberAtPaths(value, [
|
|
196
|
-
['message', 'usage', 'output_tokens'],
|
|
197
|
-
['usage', 'output_tokens'],
|
|
198
|
-
['tokens', 'output'],
|
|
199
|
-
['usage', 'tokens', 'output'],
|
|
200
|
-
['usage', 'output'],
|
|
201
|
-
['usage', 'outputTokens'],
|
|
202
|
-
['result', 'tokens', 'output'],
|
|
203
|
-
['result', 'usage', 'tokens', 'output'],
|
|
204
|
-
['result', 'usage', 'output'],
|
|
205
|
-
['result', 'usage', 'outputTokens'],
|
|
206
|
-
]),
|
|
207
|
-
usage_snapshot_cache_read_tokens: getNumberAtPaths(value, [
|
|
208
|
-
['message', 'usage', 'cache_read_input_tokens'],
|
|
209
|
-
['usage', 'cache_read_input_tokens'],
|
|
210
|
-
['tokens', 'cache', 'read'],
|
|
211
|
-
['usage', 'tokens', 'cacheRead'],
|
|
212
|
-
['usage', 'cacheRead'],
|
|
213
|
-
['usage', 'cacheReadTokens'],
|
|
214
|
-
['result', 'tokens', 'cache', 'read'],
|
|
215
|
-
['result', 'usage', 'tokens', 'cacheRead'],
|
|
216
|
-
['result', 'usage', 'cacheRead'],
|
|
217
|
-
['result', 'usage', 'cacheReadTokens'],
|
|
218
|
-
]),
|
|
219
|
-
usage_snapshot_reasoning_tokens: getNumberAtPaths(value, [
|
|
220
|
-
['tokens', 'reasoning'],
|
|
221
|
-
['usage', 'tokens', 'reasoning'],
|
|
222
|
-
['usage', 'reasoning'],
|
|
223
|
-
['usage', 'reasoningTokens'],
|
|
224
|
-
['result', 'tokens', 'reasoning'],
|
|
225
|
-
['result', 'usage', 'tokens', 'reasoning'],
|
|
226
|
-
['result', 'usage', 'reasoning'],
|
|
227
|
-
['result', 'usage', 'reasoningTokens'],
|
|
228
|
-
]),
|
|
229
|
-
usage_snapshot_cost: getNumberAtPaths(value, [
|
|
230
|
-
['total_cost_usd'],
|
|
231
|
-
['cost'],
|
|
232
|
-
['result', 'cost'],
|
|
233
|
-
]),
|
|
234
|
-
};
|
|
235
|
-
};
|
|
236
|
-
export const mergeUsageFromJsonLine = (current, line) => {
|
|
237
|
-
const trimmedLine = line.trim();
|
|
238
|
-
if (!trimmedLine.startsWith('{')) {
|
|
239
|
-
return current;
|
|
240
|
-
}
|
|
241
|
-
const { jsonObjects } = extractLeadingOpencodeJsonObjects(trimmedLine);
|
|
146
|
+
const completeUsageTotal = (current) => {
|
|
242
147
|
let next = current;
|
|
243
|
-
for (const jsonObject of jsonObjects) {
|
|
244
|
-
try {
|
|
245
|
-
next = mergeChatUsageSnapshot(next, extractChatUsageSnapshot(JSON.parse(jsonObject)));
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
148
|
if (next.usage_snapshot_total_tokens === null) {
|
|
252
149
|
const components = [
|
|
253
150
|
next.usage_snapshot_input_tokens,
|
|
@@ -264,6 +161,43 @@ export const mergeUsageFromJsonLine = (current, line) => {
|
|
|
264
161
|
}
|
|
265
162
|
return next;
|
|
266
163
|
};
|
|
164
|
+
const mergeStreamUsage = (current, usage) => mergeChatUsageSnapshot(current, {
|
|
165
|
+
usage_snapshot_context_window: usage.contextWindow,
|
|
166
|
+
usage_snapshot_max_output_tokens: usage.maxOutputTokens,
|
|
167
|
+
usage_snapshot_total_tokens: usage.totalTokens,
|
|
168
|
+
usage_snapshot_input_tokens: usage.inputTokens,
|
|
169
|
+
usage_snapshot_output_tokens: usage.outputTokens,
|
|
170
|
+
usage_snapshot_cache_read_tokens: usage.cacheReadTokens,
|
|
171
|
+
usage_snapshot_reasoning_tokens: usage.reasoningTokens,
|
|
172
|
+
usage_snapshot_cost: usage.cost,
|
|
173
|
+
});
|
|
174
|
+
export const mergeUsageFromJsonLine = (current, line) => {
|
|
175
|
+
const trimmedLine = line.trim();
|
|
176
|
+
if (!trimmedLine.startsWith('{')) {
|
|
177
|
+
return current;
|
|
178
|
+
}
|
|
179
|
+
let next = current;
|
|
180
|
+
for (const event of parseOpencodeStreamLine(trimmedLine).events) {
|
|
181
|
+
next = mergeStreamUsage(next, event.usage);
|
|
182
|
+
}
|
|
183
|
+
return completeUsageTotal(next);
|
|
184
|
+
};
|
|
185
|
+
const mergeClaudeUsageFromJsonLine = (current, line) => {
|
|
186
|
+
const { jsonObjects } = extractLeadingOpencodeJsonObjects(line);
|
|
187
|
+
let next = current;
|
|
188
|
+
for (const jsonObject of jsonObjects) {
|
|
189
|
+
try {
|
|
190
|
+
const event = JSON.parse(jsonObject);
|
|
191
|
+
if (event.type === 'assistant' || event.type === 'result') {
|
|
192
|
+
next = mergeStreamUsage(next, extractOpencodeStreamUsage(event));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Stream parsing handles malformed output separately.
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return completeUsageTotal(next);
|
|
200
|
+
};
|
|
267
201
|
const hasMatchingUsageSnapshot = (current, next) => {
|
|
268
202
|
return current.usage_snapshot_context_window === next.usage_snapshot_context_window
|
|
269
203
|
&& current.usage_snapshot_max_output_tokens === next.usage_snapshot_max_output_tokens
|
|
@@ -525,7 +459,7 @@ const updateChatSessionOpencodeMetadata = (chatSessionId, sessionId, title, io)
|
|
|
525
459
|
io.emit('chat-session:updated', chatSession);
|
|
526
460
|
}
|
|
527
461
|
};
|
|
528
|
-
const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, initialSessionId = null, sessionOperations
|
|
462
|
+
const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, cwd, initialSessionId = null, sessionOperations, }) => {
|
|
529
463
|
let lookupTimer = null;
|
|
530
464
|
let stopped = false;
|
|
531
465
|
let knownSessionId = initialSessionId;
|
|
@@ -545,7 +479,19 @@ const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, in
|
|
|
545
479
|
if (spawnTimestamp === null) {
|
|
546
480
|
return null;
|
|
547
481
|
}
|
|
548
|
-
return sessionOperations.findLatestSessionAfter(spawnTimestamp);
|
|
482
|
+
return sessionOperations.findLatestSessionAfter(spawnTimestamp, { directory: cwd });
|
|
483
|
+
};
|
|
484
|
+
const pinSessionId = (sessionId) => {
|
|
485
|
+
const normalized = sessionId?.trim();
|
|
486
|
+
if (!normalized) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
knownSessionId = normalized;
|
|
490
|
+
if (normalized !== lastAppliedSessionId) {
|
|
491
|
+
updateChatSessionOpencodeMetadata(chatSessionId, normalized, null, io);
|
|
492
|
+
lastAppliedSessionId = normalized;
|
|
493
|
+
lastAppliedTitle = null;
|
|
494
|
+
}
|
|
549
495
|
};
|
|
550
496
|
const refresh = async () => {
|
|
551
497
|
if (stopped) {
|
|
@@ -586,6 +532,7 @@ const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, in
|
|
|
586
532
|
}, OPENCODE_SESSION_METADATA_LOOKUP_DELAY_MS);
|
|
587
533
|
};
|
|
588
534
|
return {
|
|
535
|
+
pinSessionId,
|
|
589
536
|
refresh,
|
|
590
537
|
scheduleRetry,
|
|
591
538
|
stop: () => {
|
|
@@ -594,7 +541,7 @@ const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, in
|
|
|
594
541
|
},
|
|
595
542
|
};
|
|
596
543
|
};
|
|
597
|
-
const createSpecOpencodeMetadataRefresher = ({ specSessionId, io, spawnTimestamp, initialSessionId = null, }) => {
|
|
544
|
+
const createSpecOpencodeMetadataRefresher = ({ specSessionId, io, spawnTimestamp, cwd, initialSessionId = null, sessionOperations, }) => {
|
|
598
545
|
let lookupTimer = null;
|
|
599
546
|
let stopped = false;
|
|
600
547
|
let knownSessionId = initialSessionId;
|
|
@@ -609,12 +556,24 @@ const createSpecOpencodeMetadataRefresher = ({ specSessionId, io, spawnTimestamp
|
|
|
609
556
|
};
|
|
610
557
|
const readCurrentSession = async () => {
|
|
611
558
|
if (knownSessionId) {
|
|
612
|
-
return
|
|
559
|
+
return sessionOperations.findSessionById(knownSessionId);
|
|
613
560
|
}
|
|
614
561
|
if (spawnTimestamp === null) {
|
|
615
562
|
return null;
|
|
616
563
|
}
|
|
617
|
-
return
|
|
564
|
+
return sessionOperations.findLatestSessionAfter(spawnTimestamp, { directory: cwd });
|
|
565
|
+
};
|
|
566
|
+
const pinSessionId = (sessionId) => {
|
|
567
|
+
const normalized = sessionId?.trim();
|
|
568
|
+
if (!normalized) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
knownSessionId = normalized;
|
|
572
|
+
if (normalized !== lastAppliedSessionId) {
|
|
573
|
+
updateSpecSessionOpencodeMetadata(specSessionId, normalized, null, io);
|
|
574
|
+
lastAppliedSessionId = normalized;
|
|
575
|
+
lastAppliedTitle = null;
|
|
576
|
+
}
|
|
618
577
|
};
|
|
619
578
|
const refresh = async () => {
|
|
620
579
|
if (stopped) {
|
|
@@ -655,6 +614,7 @@ const createSpecOpencodeMetadataRefresher = ({ specSessionId, io, spawnTimestamp
|
|
|
655
614
|
}, OPENCODE_SESSION_METADATA_LOOKUP_DELAY_MS);
|
|
656
615
|
};
|
|
657
616
|
return {
|
|
617
|
+
pinSessionId,
|
|
658
618
|
refresh,
|
|
659
619
|
scheduleRetry,
|
|
660
620
|
stop: () => {
|
|
@@ -977,60 +937,40 @@ const parseClaudeStreamJsonLine = (line, cwd) => {
|
|
|
977
937
|
}
|
|
978
938
|
return emptyParsedStreamLine();
|
|
979
939
|
};
|
|
980
|
-
const formatOpencodeToolUse = (event, cwd) => {
|
|
981
|
-
const toolName = event.part?.tool;
|
|
982
|
-
if (!toolName) {
|
|
983
|
-
return null;
|
|
984
|
-
}
|
|
985
|
-
return formatToolCall(toolName, event.part?.state?.input, cwd);
|
|
986
|
-
};
|
|
987
|
-
const formatOpencodeStepTitle = (event) => {
|
|
988
|
-
const title = event.part?.title?.trim();
|
|
989
|
-
return title || null;
|
|
990
|
-
};
|
|
991
940
|
const parseOpencodeJsonEvent = (event, cwd) => {
|
|
992
|
-
const providerSessionId = event.
|
|
993
|
-
|| event.sessionId?.trim()
|
|
994
|
-
|| event.part?.sessionID?.trim()
|
|
995
|
-
|| event.part?.sessionId?.trim()
|
|
996
|
-
|| null;
|
|
941
|
+
const providerSessionId = event.sessionId;
|
|
997
942
|
if (event.type === 'subagent_snapshot') {
|
|
998
|
-
const sessionId = typeof event.part?.state?.metadata?.sessionId === 'string'
|
|
999
|
-
? event.part.state.metadata.sessionId
|
|
1000
|
-
: null;
|
|
1001
943
|
return {
|
|
1002
944
|
...emptyParsedStreamLine(),
|
|
1003
945
|
providerSessionId,
|
|
1004
946
|
subagentEvents: [{
|
|
1005
947
|
kind: 'snapshot',
|
|
1006
|
-
callId: event.
|
|
1007
|
-
label: event.
|
|
1008
|
-
sessionId,
|
|
1009
|
-
status: getSubagentCompletionStatus(event.
|
|
1010
|
-
log: event.
|
|
1011
|
-
response: event.
|
|
948
|
+
callId: event.tool.callId,
|
|
949
|
+
label: event.tool.title || 'Subagent',
|
|
950
|
+
sessionId: event.tool.childSessionId,
|
|
951
|
+
status: getSubagentCompletionStatus(event.tool.status),
|
|
952
|
+
log: event.tool.log,
|
|
953
|
+
response: event.tool.response,
|
|
1012
954
|
}],
|
|
1013
955
|
};
|
|
1014
956
|
}
|
|
1015
|
-
if (event.type === 'plan' && event.
|
|
957
|
+
if (event.type === 'plan' && event.text) {
|
|
1016
958
|
return {
|
|
1017
|
-
logFragment: event.
|
|
959
|
+
logFragment: event.text,
|
|
1018
960
|
responseFragment: null,
|
|
1019
961
|
subagentEvents: [],
|
|
1020
962
|
providerSessionId,
|
|
1021
963
|
modelResolutionError: false,
|
|
1022
|
-
concatenateFragments: event.
|
|
1023
|
-
replaceFragment: event.
|
|
1024
|
-
fragmentGroup: event.
|
|
1025
|
-
?? event.part.metadata?.codex?.itemId
|
|
1026
|
-
?? null,
|
|
964
|
+
concatenateFragments: event.metadata.delta,
|
|
965
|
+
replaceFragment: event.metadata.snapshot,
|
|
966
|
+
fragmentGroup: event.metadata.fragmentGroup ?? event.metadata.itemId,
|
|
1027
967
|
};
|
|
1028
968
|
}
|
|
1029
|
-
if (event.type === 'text' && event.
|
|
1030
|
-
const concatenateFragments = event.
|
|
1031
|
-
const text = concatenateFragments ? event.
|
|
1032
|
-
const phase = event.
|
|
1033
|
-
const recoveryEvent = interpretOpencodeRecoveryEvent(event.type,
|
|
969
|
+
if (event.type === 'text' && event.text) {
|
|
970
|
+
const concatenateFragments = event.metadata.delta;
|
|
971
|
+
const text = concatenateFragments ? event.text : event.text.trim();
|
|
972
|
+
const phase = event.metadata.phase;
|
|
973
|
+
const recoveryEvent = interpretOpencodeRecoveryEvent(event.type, undefined, text, phase);
|
|
1034
974
|
return {
|
|
1035
975
|
logFragment: text || null,
|
|
1036
976
|
responseFragment: text && (!phase || phase === 'final_answer') ? text : null,
|
|
@@ -1039,14 +979,12 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1039
979
|
modelResolutionError: false,
|
|
1040
980
|
recoveryEvents: recoveryEvent ? [recoveryEvent] : [],
|
|
1041
981
|
concatenateFragments,
|
|
1042
|
-
fragmentGroup: event.
|
|
1043
|
-
?? event.part.metadata?.codex?.itemId
|
|
1044
|
-
?? null,
|
|
982
|
+
fragmentGroup: event.metadata.fragmentGroup ?? event.metadata.itemId,
|
|
1045
983
|
};
|
|
1046
984
|
}
|
|
1047
|
-
if (event.type === 'reasoning'
|
|
1048
|
-
const concatenateFragments = event.
|
|
1049
|
-
const text = concatenateFragments ? event.
|
|
985
|
+
if (event.type === 'reasoning') {
|
|
986
|
+
const concatenateFragments = event.metadata.delta;
|
|
987
|
+
const text = concatenateFragments ? event.text : event.text.trim();
|
|
1050
988
|
return {
|
|
1051
989
|
logFragment: text || null,
|
|
1052
990
|
responseFragment: null,
|
|
@@ -1054,26 +992,22 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1054
992
|
providerSessionId,
|
|
1055
993
|
modelResolutionError: false,
|
|
1056
994
|
concatenateFragments,
|
|
1057
|
-
fragmentGroup: event.
|
|
1058
|
-
?? event.part.metadata?.codex?.itemId
|
|
1059
|
-
?? null,
|
|
995
|
+
fragmentGroup: event.metadata.fragmentGroup ?? event.metadata.itemId,
|
|
1060
996
|
};
|
|
1061
997
|
}
|
|
1062
|
-
if (event.type === '
|
|
1063
|
-
const isToolInvocation = event.type === '
|
|
1064
|
-
const toolName = event.
|
|
998
|
+
if (event.type === 'tool_use' || event.type === 'tool_result') {
|
|
999
|
+
const isToolInvocation = event.type === 'tool_use';
|
|
1000
|
+
const toolName = event.tool.name;
|
|
1065
1001
|
if (toolName && isSubagentTool(toolName)) {
|
|
1066
|
-
const description = getSubagentDescription(event.
|
|
1067
|
-
const rawOutput = event.
|
|
1002
|
+
const description = getSubagentDescription(event.tool.input, toolName);
|
|
1003
|
+
const rawOutput = event.tool.output?.trim();
|
|
1068
1004
|
const envelope = parseOpencodeTaskEnvelope(rawOutput);
|
|
1069
1005
|
const output = envelope ? envelope.result : rawOutput;
|
|
1070
|
-
const taskStatus = event.
|
|
1006
|
+
const taskStatus = event.tool.status?.toLowerCase() ?? envelope?.state?.toLowerCase();
|
|
1071
1007
|
const isTerminal = Boolean(taskStatus && taskStatus !== 'running' && taskStatus !== 'pending');
|
|
1072
1008
|
const outputKind = isTerminal ? 'complete' : 'fragment';
|
|
1073
1009
|
const completionStatus = getSubagentCompletionStatus(taskStatus);
|
|
1074
|
-
const childSessionId = envelope?.childSessionId
|
|
1075
|
-
?? (typeof event.part?.state?.metadata?.sessionID === 'string' ? event.part.state.metadata.sessionID : null)
|
|
1076
|
-
?? (typeof event.part?.state?.metadata?.sessionId === 'string' ? event.part.state.metadata.sessionId : null);
|
|
1010
|
+
const childSessionId = envelope?.childSessionId ?? event.tool.childSessionId;
|
|
1077
1011
|
return {
|
|
1078
1012
|
logFragment: isToolInvocation ? `Subagent: ${description}` : null,
|
|
1079
1013
|
responseFragment: null,
|
|
@@ -1081,7 +1015,7 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1081
1015
|
...(isToolInvocation
|
|
1082
1016
|
? [{
|
|
1083
1017
|
kind: 'start',
|
|
1084
|
-
callId: event.
|
|
1018
|
+
callId: event.tool.callId,
|
|
1085
1019
|
label: `Subagent: ${description}`,
|
|
1086
1020
|
sessionId: childSessionId,
|
|
1087
1021
|
}]
|
|
@@ -1089,7 +1023,7 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1089
1023
|
...(output || isTerminal
|
|
1090
1024
|
? [{
|
|
1091
1025
|
kind: outputKind,
|
|
1092
|
-
callId: event.
|
|
1026
|
+
callId: event.tool.callId,
|
|
1093
1027
|
label: `Subagent: ${description}`,
|
|
1094
1028
|
...(output ? { text: output } : {}),
|
|
1095
1029
|
sessionId: childSessionId,
|
|
@@ -1104,7 +1038,7 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1104
1038
|
if (!isToolInvocation) {
|
|
1105
1039
|
return { ...emptyParsedStreamLine(), providerSessionId };
|
|
1106
1040
|
}
|
|
1107
|
-
const toolFragment =
|
|
1041
|
+
const toolFragment = formatToolCall(toolName, event.tool.input, cwd);
|
|
1108
1042
|
return {
|
|
1109
1043
|
logFragment: toolFragment,
|
|
1110
1044
|
responseFragment: null,
|
|
@@ -1115,7 +1049,7 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1115
1049
|
}
|
|
1116
1050
|
if (event.type === 'step_start') {
|
|
1117
1051
|
return {
|
|
1118
|
-
logFragment:
|
|
1052
|
+
logFragment: event.title,
|
|
1119
1053
|
responseFragment: null,
|
|
1120
1054
|
subagentEvents: [],
|
|
1121
1055
|
providerSessionId,
|
|
@@ -1132,7 +1066,7 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1132
1066
|
};
|
|
1133
1067
|
}
|
|
1134
1068
|
if (event.type === 'error') {
|
|
1135
|
-
const recoveryEvent = interpretOpencodeRecoveryEvent(event.type, event.error, event.
|
|
1069
|
+
const recoveryEvent = interpretOpencodeRecoveryEvent(event.type, event.error, event.fallbackText ?? undefined, undefined);
|
|
1136
1070
|
const parsedError = recoveryEvent?.type === 'error' ? recoveryEvent.error : null;
|
|
1137
1071
|
const text = parsedError?.message ?? '';
|
|
1138
1072
|
return {
|
|
@@ -1143,15 +1077,16 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
1143
1077
|
recoveryEvents: recoveryEvent ? [recoveryEvent] : [],
|
|
1144
1078
|
};
|
|
1145
1079
|
}
|
|
1146
|
-
return emptyParsedStreamLine();
|
|
1080
|
+
return { ...emptyParsedStreamLine(), providerSessionId };
|
|
1147
1081
|
};
|
|
1148
1082
|
const parseOpencodeJsonLine = (line, cwd) => {
|
|
1149
1083
|
const trimmed = line.trim();
|
|
1150
1084
|
if (!trimmed) {
|
|
1151
1085
|
return emptyParsedStreamLine();
|
|
1152
1086
|
}
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1087
|
+
const normalizedLine = parseOpencodeStreamLine(trimmed);
|
|
1088
|
+
const { events, remainder } = normalizedLine;
|
|
1089
|
+
if (!normalizedLine.structured) {
|
|
1155
1090
|
if (trimmed.startsWith('{')) {
|
|
1156
1091
|
return emptyParsedStreamLine();
|
|
1157
1092
|
}
|
|
@@ -1179,21 +1114,14 @@ const parseOpencodeJsonLine = (line, cwd) => {
|
|
|
1179
1114
|
let concatenateFragments;
|
|
1180
1115
|
let replaceFragment;
|
|
1181
1116
|
let fragmentGroup;
|
|
1182
|
-
for (const
|
|
1183
|
-
let event;
|
|
1184
|
-
try {
|
|
1185
|
-
event = JSON.parse(jsonObject);
|
|
1186
|
-
}
|
|
1187
|
-
catch {
|
|
1188
|
-
continue;
|
|
1189
|
-
}
|
|
1117
|
+
for (const event of events) {
|
|
1190
1118
|
const parsedEvent = parseOpencodeJsonEvent(event, cwd);
|
|
1191
1119
|
const { logFragment, responseFragment } = parsedEvent;
|
|
1192
1120
|
subagentEvents.push(...parsedEvent.subagentEvents);
|
|
1193
1121
|
recoveryEvents.push(...(parsedEvent.recoveryEvents ?? []));
|
|
1194
1122
|
modelResolutionError ||= Boolean(parsedEvent.modelResolutionError);
|
|
1195
1123
|
providerSessionId = providerSessionId ?? parsedEvent.providerSessionId;
|
|
1196
|
-
if (
|
|
1124
|
+
if (events.length === 1) {
|
|
1197
1125
|
concatenateFragments = parsedEvent.concatenateFragments;
|
|
1198
1126
|
replaceFragment = parsedEvent.replaceFragment;
|
|
1199
1127
|
fragmentGroup = parsedEvent.fragmentGroup;
|
|
@@ -1236,6 +1164,7 @@ const parseOpencodeJsonLine = (line, cwd) => {
|
|
|
1236
1164
|
}
|
|
1237
1165
|
return emptyParsedStreamLine();
|
|
1238
1166
|
};
|
|
1167
|
+
const hasOpencodeFinalAnswer = (line) => parseOpencodeStreamLine(line).events.some(event => (event.type === 'text' && Boolean(event.text) && (!event.metadata.phase || event.metadata.phase === 'final_answer')));
|
|
1239
1168
|
const appendTranscriptText = (current, fragment) => {
|
|
1240
1169
|
const trimmed = fragment.trim();
|
|
1241
1170
|
if (!trimmed) {
|
|
@@ -1289,7 +1218,7 @@ const appendOpencodeTranscriptLine = ({ parsed, logLines, responseLines, recover
|
|
|
1289
1218
|
}
|
|
1290
1219
|
return shouldResetResponse;
|
|
1291
1220
|
};
|
|
1292
|
-
export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, executionContext, deferMainFinalization = false, }) => {
|
|
1221
|
+
export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, executionContext, deferMainFinalization = false, opencodeRuntimeIdentity = null, }) => {
|
|
1293
1222
|
const trackedRuns = [];
|
|
1294
1223
|
let nextRunIndex = Math.max(executionContext ? getNextAgentRunIndex(executionContext.runId) : getNextAgentRunIndex(parentType, parentId), mainRun ? mainRun.runIndex + 1 : 1);
|
|
1295
1224
|
const emitUpdate = () => {
|
|
@@ -1335,7 +1264,7 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1335
1264
|
return;
|
|
1336
1265
|
}
|
|
1337
1266
|
run.sessionId = normalized;
|
|
1338
|
-
updateAgentRunSessionId(run.id, normalized);
|
|
1267
|
+
updateAgentRunSessionId(run.id, normalized, opencodeRuntimeIdentity);
|
|
1339
1268
|
};
|
|
1340
1269
|
const updateLabel = (run, label) => {
|
|
1341
1270
|
if (!label?.trim()) {
|
|
@@ -1507,6 +1436,8 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1507
1436
|
response_text: run.response || null,
|
|
1508
1437
|
failure_message: persistedRun?.failure_message ?? null,
|
|
1509
1438
|
session_id: run.sessionId,
|
|
1439
|
+
opencode_session_runtime_major: persistedRun?.opencode_session_runtime_major ?? null,
|
|
1440
|
+
opencode_session_runtime_version: persistedRun?.opencode_session_runtime_version ?? null,
|
|
1510
1441
|
completed_at: persistedRun?.completed_at ?? timestamp,
|
|
1511
1442
|
created_at: persistedRun?.created_at ?? run.createdAt,
|
|
1512
1443
|
updated_at: persistedRun?.updated_at ?? timestamp,
|
|
@@ -1538,6 +1469,8 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1538
1469
|
response_text: parentResponse,
|
|
1539
1470
|
failure_message: persistedMainRun?.failure_message ?? null,
|
|
1540
1471
|
session_id: persistedMainRun?.session_id ?? null,
|
|
1472
|
+
opencode_session_runtime_major: persistedMainRun?.opencode_session_runtime_major ?? null,
|
|
1473
|
+
opencode_session_runtime_version: persistedMainRun?.opencode_session_runtime_version ?? null,
|
|
1541
1474
|
completed_at: persistedMainRun?.completed_at ?? timestamp,
|
|
1542
1475
|
created_at: persistedMainRun?.created_at ?? timestamp,
|
|
1543
1476
|
updated_at: persistedMainRun?.updated_at ?? timestamp,
|
|
@@ -1565,6 +1498,8 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1565
1498
|
response_text: parentResponse,
|
|
1566
1499
|
failure_message: null,
|
|
1567
1500
|
session_id: null,
|
|
1501
|
+
opencode_session_runtime_major: null,
|
|
1502
|
+
opencode_session_runtime_version: null,
|
|
1568
1503
|
completed_at: timestamp,
|
|
1569
1504
|
created_at: timestamp,
|
|
1570
1505
|
updated_at: timestamp,
|
|
@@ -1575,7 +1510,7 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1575
1510
|
const setMainSessionId = (sessionId) => {
|
|
1576
1511
|
const normalized = sessionId?.trim();
|
|
1577
1512
|
if (mainRun && normalized) {
|
|
1578
|
-
updateAgentRunSessionId(mainRun.id, normalized);
|
|
1513
|
+
updateAgentRunSessionId(mainRun.id, normalized, opencodeRuntimeIdentity);
|
|
1579
1514
|
}
|
|
1580
1515
|
};
|
|
1581
1516
|
return { applySnapshot, handle, finalize, hasRunningSubagents, setMainSessionId, updateMain };
|
|
@@ -1598,7 +1533,7 @@ export const reconstructOpencodeSubagentTranscript = (parts, cwd) => {
|
|
|
1598
1533
|
: partType === 'step-finish'
|
|
1599
1534
|
? 'step_finish'
|
|
1600
1535
|
: partType;
|
|
1601
|
-
const parsed = parseOpencodeJsonEvent({ type: eventType, part }, cwd);
|
|
1536
|
+
const parsed = parseOpencodeJsonEvent(decodeOpencodeStreamEvent({ type: eventType, part }), cwd);
|
|
1602
1537
|
if (parsed.logFragment && parsed.logFragment !== parsed.responseFragment) {
|
|
1603
1538
|
log = appendTranscriptText(log, parsed.logFragment);
|
|
1604
1539
|
}
|
|
@@ -1620,7 +1555,7 @@ export const reconstructOpencodeSubagentTranscript = (parts, cwd) => {
|
|
|
1620
1555
|
return { finalResponse: finalResponse || null, log: log || null, response: response || null };
|
|
1621
1556
|
};
|
|
1622
1557
|
export const readOpencodeSessionTranscript = async ({ sessionId, since, cwd, sessionOperations = hostOpencodeSessionOperations, }) => {
|
|
1623
|
-
const reader = createOpencodeTaskSessionReader(sessionOperations);
|
|
1558
|
+
const reader = createOpencodeTaskSessionReader(sessionOperations, { directory: cwd });
|
|
1624
1559
|
if (!reader) {
|
|
1625
1560
|
return null;
|
|
1626
1561
|
}
|
|
@@ -1635,7 +1570,9 @@ export const readOpencodeSessionTranscript = async ({ sessionId, since, cwd, ses
|
|
|
1635
1570
|
}
|
|
1636
1571
|
};
|
|
1637
1572
|
export const createOpencodeSubagentMonitor = ({ initialParentSessionId, spawnTimestamp, cwd, tracker, onParentSessionId, onActivity, taskSessionReader, discoverParentSession = true, parentDiscoveryDelayMs = 0, }) => {
|
|
1638
|
-
const reader = taskSessionReader === undefined
|
|
1573
|
+
const reader = taskSessionReader === undefined
|
|
1574
|
+
? createOpencodeTaskSessionReader(hostOpencodeSessionOperations, { directory: cwd })
|
|
1575
|
+
: taskSessionReader;
|
|
1639
1576
|
let parentSessionId = initialParentSessionId;
|
|
1640
1577
|
let timer = null;
|
|
1641
1578
|
const childSessionByCallId = new Map();
|
|
@@ -2079,8 +2016,10 @@ export const stopSpecReviewAgent = async (specReviewRunId) => {
|
|
|
2079
2016
|
}
|
|
2080
2017
|
return true;
|
|
2081
2018
|
};
|
|
2082
|
-
const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null, skills = [], resumeSessionId = null, sessionField = 'session_id', trackBuildProcess = false, reviewProcessState, emitSuccessUpdate = true, opencodeAgentMode, runStream, executionContext, persistSessionIdToTicket = false, spawnPermitId, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
2019
|
+
const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null, skills = [], resumeSessionId = null, sessionField = 'session_id', trackBuildProcess = false, reviewProcessState, emitSuccessUpdate = true, opencodeAgentMode, runStream, executionContext, persistSessionIdToTicket = false, spawnPermitId, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), opencodeRuntimeIdentity = null, }) => {
|
|
2083
2020
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
2021
|
+
const resolvedOpencodeCompatibility = resolveOpencodeCompatibility(tool, executionProvider, opencodeRuntimeIdentity);
|
|
2022
|
+
const resolvedOpencodeRuntimeIdentity = resolvedOpencodeCompatibility?.runtimeIdentity ?? null;
|
|
2084
2023
|
const opencodeSessionOperations = executionProvider.sessionOperations?.opencode ?? null;
|
|
2085
2024
|
const subagentTracker = createSubagentRunTracker({
|
|
2086
2025
|
parentType: 'ticket',
|
|
@@ -2097,6 +2036,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2097
2036
|
}
|
|
2098
2037
|
: {}),
|
|
2099
2038
|
executionContext,
|
|
2039
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
2100
2040
|
});
|
|
2101
2041
|
const emitRunUpdate = () => {
|
|
2102
2042
|
const emitter = io;
|
|
@@ -2116,7 +2056,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2116
2056
|
};
|
|
2117
2057
|
const writeRunSessionId = (sessionId) => {
|
|
2118
2058
|
if (runStream) {
|
|
2119
|
-
updateAgentRunSessionId(runStream.runId, sessionId);
|
|
2059
|
+
updateAgentRunSessionId(runStream.runId, sessionId, resolvedOpencodeRuntimeIdentity);
|
|
2120
2060
|
emitRunUpdate();
|
|
2121
2061
|
if (persistSessionIdToTicket) {
|
|
2122
2062
|
updateSessionId(ticketId, sessionId, sessionField, io);
|
|
@@ -2201,7 +2141,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2201
2141
|
return hasResolvedOpencodeSessionId;
|
|
2202
2142
|
}
|
|
2203
2143
|
try {
|
|
2204
|
-
const session = await opencodeSessionOperations?.findLatestSessionAfter(spawnTimestamp, cwd);
|
|
2144
|
+
const session = await opencodeSessionOperations?.findLatestSessionAfter(spawnTimestamp, { directory: cwd });
|
|
2205
2145
|
if (!hasResolvedOpencodeSessionId && session?.id) {
|
|
2206
2146
|
bindResolvedOpencodeSession(session.id);
|
|
2207
2147
|
}
|
|
@@ -2221,6 +2161,8 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2221
2161
|
mode: opencodeAgentMode,
|
|
2222
2162
|
resumeSessionId,
|
|
2223
2163
|
sessionId: randomUUID(),
|
|
2164
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
2165
|
+
opencodeCompatibility: resolvedOpencodeCompatibility,
|
|
2224
2166
|
};
|
|
2225
2167
|
const invocation = agentProvider.buildStreamingInvocation(streamingRequest);
|
|
2226
2168
|
sessionId = invocation.sessionId;
|
|
@@ -2316,7 +2258,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2316
2258
|
startTime = Date.now();
|
|
2317
2259
|
if (tool === 'opencode' && spawnTimestamp !== null) {
|
|
2318
2260
|
const taskSessionReader = opencodeSessionOperations
|
|
2319
|
-
? createOpencodeTaskSessionReader(opencodeSessionOperations)
|
|
2261
|
+
? createOpencodeTaskSessionReader(opencodeSessionOperations, { directory: cwd })
|
|
2320
2262
|
: null;
|
|
2321
2263
|
opencodeSubagentMonitor = createOpencodeSubagentMonitor({
|
|
2322
2264
|
initialParentSessionId: resumeSessionId,
|
|
@@ -2349,7 +2291,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2349
2291
|
const lines = text.split('\n');
|
|
2350
2292
|
remainder = lines.pop() ?? '';
|
|
2351
2293
|
for (const line of lines) {
|
|
2352
|
-
usageSnapshot =
|
|
2294
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, line);
|
|
2353
2295
|
const parsed = parseClaudeStreamJsonLine(line, cwd);
|
|
2354
2296
|
const { logFragment, responseFragment, subagentEvents } = parsed;
|
|
2355
2297
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
@@ -2375,7 +2317,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2375
2317
|
return;
|
|
2376
2318
|
}
|
|
2377
2319
|
if (remainder.trim()) {
|
|
2378
|
-
usageSnapshot =
|
|
2320
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2379
2321
|
const parsed = parseClaudeStreamJsonLine(remainder, cwd);
|
|
2380
2322
|
const { logFragment, responseFragment, subagentEvents } = parsed;
|
|
2381
2323
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
@@ -2464,18 +2406,8 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2464
2406
|
for (const line of lines) {
|
|
2465
2407
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
2466
2408
|
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
try {
|
|
2470
|
-
const event = JSON.parse(trimmedLine);
|
|
2471
|
-
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
2472
|
-
const phase = event.part.metadata?.openai?.phase;
|
|
2473
|
-
if (!phase || phase === 'final_answer') {
|
|
2474
|
-
firstFinalAnswerTime = Date.now();
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
catch { /* best-effort timing */ }
|
|
2409
|
+
if (hasOpencodeFinalAnswer(line)) {
|
|
2410
|
+
firstFinalAnswerTime = Date.now();
|
|
2479
2411
|
}
|
|
2480
2412
|
}
|
|
2481
2413
|
const parsed = parseOpencodeJsonLine(line);
|
|
@@ -2670,8 +2602,10 @@ export const runAgent = (options) => {
|
|
|
2670
2602
|
const enrichedPrompt = enrichPromptWithPreview(options.prompt, options.executionContext, options.executionTarget);
|
|
2671
2603
|
return typeof enrichedPrompt === 'string' ? launch(enrichedPrompt) : enrichedPrompt.then(launch);
|
|
2672
2604
|
};
|
|
2673
|
-
const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], resumeSessionId = null, opencodeAgentMode = 'build', executionContext, mainRun, spawnPermitId, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
2605
|
+
const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], resumeSessionId = null, opencodeAgentMode = 'build', executionContext, mainRun, spawnPermitId, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), opencodeRuntimeIdentity = null, }) => {
|
|
2674
2606
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
2607
|
+
const resolvedOpencodeCompatibility = resolveOpencodeCompatibility(tool, executionProvider, opencodeRuntimeIdentity);
|
|
2608
|
+
const resolvedOpencodeRuntimeIdentity = resolvedOpencodeCompatibility?.runtimeIdentity ?? null;
|
|
2675
2609
|
const opencodeSessionOperations = executionProvider.sessionOperations?.opencode ?? null;
|
|
2676
2610
|
const subagentTracker = createSubagentRunTracker({
|
|
2677
2611
|
parentType: 'chat',
|
|
@@ -2680,6 +2614,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2680
2614
|
executionContext,
|
|
2681
2615
|
mainRun,
|
|
2682
2616
|
deferMainFinalization: true,
|
|
2617
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
2683
2618
|
});
|
|
2684
2619
|
return new Promise((resolve, reject) => {
|
|
2685
2620
|
let sessionId = null;
|
|
@@ -2725,6 +2660,8 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2725
2660
|
mode: opencodeAgentMode,
|
|
2726
2661
|
resumeSessionId,
|
|
2727
2662
|
sessionId: randomUUID(),
|
|
2663
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
2664
|
+
opencodeCompatibility: resolvedOpencodeCompatibility,
|
|
2728
2665
|
};
|
|
2729
2666
|
const invocation = agentProvider.buildStreamingInvocation(streamingRequest);
|
|
2730
2667
|
sessionId = invocation.sessionId;
|
|
@@ -2739,15 +2676,18 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2739
2676
|
subagentTracker.setMainSessionId(resumeSessionId);
|
|
2740
2677
|
}
|
|
2741
2678
|
spawnTimestamp = Date.now();
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2679
|
+
if (opencodeSessionOperations) {
|
|
2680
|
+
opencodeMetadataRefresher = createOpencodeMetadataRefresher({
|
|
2681
|
+
chatSessionId,
|
|
2682
|
+
io,
|
|
2683
|
+
spawnTimestamp,
|
|
2684
|
+
cwd,
|
|
2685
|
+
initialSessionId: resumeSessionId,
|
|
2686
|
+
sessionOperations: opencodeSessionOperations,
|
|
2687
|
+
});
|
|
2688
|
+
chatState.metadataRefresher = opencodeMetadataRefresher;
|
|
2689
|
+
}
|
|
2690
|
+
if (resumeSessionId && opencodeMetadataRefresher) {
|
|
2751
2691
|
const metadataRefresher = opencodeMetadataRefresher;
|
|
2752
2692
|
void metadataRefresher.refresh().then(result => {
|
|
2753
2693
|
if (!result.hasResolvedTitle) {
|
|
@@ -2834,7 +2774,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2834
2774
|
cwd,
|
|
2835
2775
|
tracker: subagentTracker,
|
|
2836
2776
|
taskSessionReader: opencodeSessionOperations
|
|
2837
|
-
? createOpencodeTaskSessionReader(opencodeSessionOperations)
|
|
2777
|
+
? createOpencodeTaskSessionReader(opencodeSessionOperations, { directory: cwd })
|
|
2838
2778
|
: null,
|
|
2839
2779
|
discoverParentSession: false,
|
|
2840
2780
|
onParentSessionId: discoveredSessionId => {
|
|
@@ -2866,7 +2806,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2866
2806
|
executionCapableEventObserved = true;
|
|
2867
2807
|
}
|
|
2868
2808
|
subagentTracker.handle(subagentEvents);
|
|
2869
|
-
usageSnapshot =
|
|
2809
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, line);
|
|
2870
2810
|
if (logFragment !== null) {
|
|
2871
2811
|
logLines.push(logFragment);
|
|
2872
2812
|
}
|
|
@@ -2886,7 +2826,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2886
2826
|
return;
|
|
2887
2827
|
}
|
|
2888
2828
|
if (remainder.trim()) {
|
|
2889
|
-
usageSnapshot =
|
|
2829
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2890
2830
|
const parsed = parseClaudeStreamJsonLine(remainder);
|
|
2891
2831
|
const { logFragment, responseFragment, subagentEvents } = parsed;
|
|
2892
2832
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
@@ -2989,18 +2929,8 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2989
2929
|
for (const line of lines) {
|
|
2990
2930
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
2991
2931
|
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
try {
|
|
2995
|
-
const event = JSON.parse(trimmedLine);
|
|
2996
|
-
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
2997
|
-
const phase = event.part.metadata?.openai?.phase;
|
|
2998
|
-
if (!phase || phase === 'final_answer') {
|
|
2999
|
-
firstFinalAnswerTime = Date.now();
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
catch { /* best-effort timing */ }
|
|
2932
|
+
if (hasOpencodeFinalAnswer(line)) {
|
|
2933
|
+
firstFinalAnswerTime = Date.now();
|
|
3004
2934
|
}
|
|
3005
2935
|
}
|
|
3006
2936
|
const parsed = parseOpencodeJsonLine(line, cwd);
|
|
@@ -3008,7 +2938,16 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3008
2938
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
3009
2939
|
executionCapableEventObserved = true;
|
|
3010
2940
|
}
|
|
3011
|
-
|
|
2941
|
+
if (providerSessionId) {
|
|
2942
|
+
if (opencodeMetadataRefresher) {
|
|
2943
|
+
opencodeMetadataRefresher.pinSessionId(providerSessionId);
|
|
2944
|
+
}
|
|
2945
|
+
else {
|
|
2946
|
+
updateChatSessionId(chatSessionId, 'opencode', providerSessionId, io);
|
|
2947
|
+
}
|
|
2948
|
+
opencodeSubagentMonitor?.setParentSessionId(providerSessionId);
|
|
2949
|
+
subagentTracker.setMainSessionId(providerSessionId);
|
|
2950
|
+
}
|
|
3012
2951
|
subagentTracker.handle(subagentEvents);
|
|
3013
2952
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
3014
2953
|
firstFinalAnswerTime = null;
|
|
@@ -3035,7 +2974,16 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3035
2974
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
3036
2975
|
executionCapableEventObserved = true;
|
|
3037
2976
|
}
|
|
3038
|
-
|
|
2977
|
+
if (providerSessionId) {
|
|
2978
|
+
if (opencodeMetadataRefresher) {
|
|
2979
|
+
opencodeMetadataRefresher.pinSessionId(providerSessionId);
|
|
2980
|
+
}
|
|
2981
|
+
else {
|
|
2982
|
+
updateChatSessionId(chatSessionId, 'opencode', providerSessionId, io);
|
|
2983
|
+
}
|
|
2984
|
+
opencodeSubagentMonitor?.setParentSessionId(providerSessionId);
|
|
2985
|
+
subagentTracker.setMainSessionId(providerSessionId);
|
|
2986
|
+
}
|
|
3039
2987
|
subagentTracker.handle(subagentEvents);
|
|
3040
2988
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
3041
2989
|
firstFinalAnswerTime = null;
|
|
@@ -3061,12 +3009,12 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3061
3009
|
?? resumeSessionId;
|
|
3062
3010
|
await opencodeSubagentMonitor?.refresh();
|
|
3063
3011
|
if (chatState.stopRequested) {
|
|
3064
|
-
if (resolvedOpencodeSessionId) {
|
|
3012
|
+
if (resolvedOpencodeSessionId && opencodeSessionOperations) {
|
|
3065
3013
|
usageSnapshot = await resolveOpencodeUsageSnapshot({
|
|
3066
3014
|
current: usageSnapshot,
|
|
3067
3015
|
sessionId: resolvedOpencodeSessionId,
|
|
3068
3016
|
spawnTimestamp,
|
|
3069
|
-
|
|
3017
|
+
sessionOperations: opencodeSessionOperations,
|
|
3070
3018
|
});
|
|
3071
3019
|
}
|
|
3072
3020
|
updateChatSessionStatus(chatSessionId, 'running', preservedOutput?.log ?? null, io, preservedOutput?.response ?? null);
|
|
@@ -3086,12 +3034,12 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3086
3034
|
log,
|
|
3087
3035
|
response: finalResponse,
|
|
3088
3036
|
});
|
|
3089
|
-
if (resolvedOpencodeSessionId && spawnTimestamp !== null) {
|
|
3037
|
+
if (resolvedOpencodeSessionId && spawnTimestamp !== null && opencodeSessionOperations) {
|
|
3090
3038
|
usageSnapshot = await resolveOpencodeUsageSnapshot({
|
|
3091
3039
|
current: usageSnapshot,
|
|
3092
3040
|
sessionId: resolvedOpencodeSessionId,
|
|
3093
3041
|
spawnTimestamp,
|
|
3094
|
-
|
|
3042
|
+
sessionOperations: opencodeSessionOperations,
|
|
3095
3043
|
});
|
|
3096
3044
|
}
|
|
3097
3045
|
updateChatSessionStatus(chatSessionId, 'running', finalOutput.log, io, finalOutput.response);
|
|
@@ -3155,8 +3103,11 @@ export const runChatAgent = (options) => {
|
|
|
3155
3103
|
const enrichedPrompt = enrichPromptWithPreview(options.prompt, options.executionContext, options.executionTarget);
|
|
3156
3104
|
return typeof enrichedPrompt === 'string' ? launch(enrichedPrompt) : enrichedPrompt.then(launch);
|
|
3157
3105
|
};
|
|
3158
|
-
const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], resumeSessionId = null, executionContext, mainRun, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
3106
|
+
const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], resumeSessionId = null, executionContext, mainRun, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), opencodeRuntimeIdentity = null, }) => {
|
|
3159
3107
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
3108
|
+
const resolvedOpencodeCompatibility = resolveOpencodeCompatibility(tool, executionProvider, opencodeRuntimeIdentity);
|
|
3109
|
+
const resolvedOpencodeRuntimeIdentity = resolvedOpencodeCompatibility?.runtimeIdentity ?? null;
|
|
3110
|
+
const opencodeSessionOperations = executionProvider.sessionOperations?.opencode ?? null;
|
|
3160
3111
|
const subagentTracker = createSubagentRunTracker({
|
|
3161
3112
|
parentType: 'spec',
|
|
3162
3113
|
parentId: specSessionId,
|
|
@@ -3164,6 +3115,7 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3164
3115
|
executionContext,
|
|
3165
3116
|
mainRun,
|
|
3166
3117
|
deferMainFinalization: true,
|
|
3118
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
3167
3119
|
});
|
|
3168
3120
|
return new Promise((resolve, reject) => {
|
|
3169
3121
|
let sessionId = null;
|
|
@@ -3209,6 +3161,8 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3209
3161
|
mode: 'plan',
|
|
3210
3162
|
resumeSessionId,
|
|
3211
3163
|
sessionId: randomUUID(),
|
|
3164
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
3165
|
+
opencodeCompatibility: resolvedOpencodeCompatibility,
|
|
3212
3166
|
};
|
|
3213
3167
|
const invocation = agentProvider.buildStreamingInvocation(streamingRequest);
|
|
3214
3168
|
sessionId = invocation.sessionId;
|
|
@@ -3223,14 +3177,18 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3223
3177
|
subagentTracker.setMainSessionId(resumeSessionId);
|
|
3224
3178
|
}
|
|
3225
3179
|
spawnTimestamp = Date.now();
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3180
|
+
if (opencodeSessionOperations) {
|
|
3181
|
+
opencodeMetadataRefresher = createSpecOpencodeMetadataRefresher({
|
|
3182
|
+
specSessionId,
|
|
3183
|
+
io,
|
|
3184
|
+
spawnTimestamp,
|
|
3185
|
+
cwd,
|
|
3186
|
+
initialSessionId: resumeSessionId,
|
|
3187
|
+
sessionOperations: opencodeSessionOperations,
|
|
3188
|
+
});
|
|
3189
|
+
specState.metadataRefresher = opencodeMetadataRefresher;
|
|
3190
|
+
}
|
|
3191
|
+
if (resumeSessionId && opencodeMetadataRefresher) {
|
|
3234
3192
|
const metadataRefresher = opencodeMetadataRefresher;
|
|
3235
3193
|
void metadataRefresher.refresh().then(result => {
|
|
3236
3194
|
if (!result.hasResolvedTitle) {
|
|
@@ -3307,6 +3265,9 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3307
3265
|
spawnTimestamp,
|
|
3308
3266
|
cwd,
|
|
3309
3267
|
tracker: subagentTracker,
|
|
3268
|
+
taskSessionReader: opencodeSessionOperations
|
|
3269
|
+
? createOpencodeTaskSessionReader(opencodeSessionOperations, { directory: cwd })
|
|
3270
|
+
: null,
|
|
3310
3271
|
discoverParentSession: false,
|
|
3311
3272
|
onParentSessionId: discoveredSessionId => {
|
|
3312
3273
|
updateSpecSessionIdLocal(specSessionId, 'opencode', discoveredSessionId, io);
|
|
@@ -3337,7 +3298,7 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3337
3298
|
executionCapableEventObserved = true;
|
|
3338
3299
|
}
|
|
3339
3300
|
subagentTracker.handle(subagentEvents);
|
|
3340
|
-
usageSnapshot =
|
|
3301
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, line);
|
|
3341
3302
|
if (logFragment !== null) {
|
|
3342
3303
|
logLines.push(logFragment);
|
|
3343
3304
|
}
|
|
@@ -3358,7 +3319,7 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3358
3319
|
return;
|
|
3359
3320
|
}
|
|
3360
3321
|
if (remainder.trim()) {
|
|
3361
|
-
usageSnapshot =
|
|
3322
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, remainder);
|
|
3362
3323
|
const parsed = parseClaudeStreamJsonLine(remainder);
|
|
3363
3324
|
const { logFragment, responseFragment, subagentEvents } = parsed;
|
|
3364
3325
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
@@ -3461,18 +3422,8 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3461
3422
|
for (const line of lines) {
|
|
3462
3423
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
3463
3424
|
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
try {
|
|
3467
|
-
const event = JSON.parse(trimmedLine);
|
|
3468
|
-
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
3469
|
-
const phase = event.part.metadata?.openai?.phase;
|
|
3470
|
-
if (!phase || phase === 'final_answer') {
|
|
3471
|
-
firstFinalAnswerTime = Date.now();
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
}
|
|
3475
|
-
catch { /* best-effort timing */ }
|
|
3425
|
+
if (hasOpencodeFinalAnswer(line)) {
|
|
3426
|
+
firstFinalAnswerTime = Date.now();
|
|
3476
3427
|
}
|
|
3477
3428
|
}
|
|
3478
3429
|
const parsed = parseOpencodeJsonLine(line, cwd);
|
|
@@ -3480,7 +3431,16 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3480
3431
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
3481
3432
|
executionCapableEventObserved = true;
|
|
3482
3433
|
}
|
|
3483
|
-
|
|
3434
|
+
if (providerSessionId) {
|
|
3435
|
+
if (opencodeMetadataRefresher) {
|
|
3436
|
+
opencodeMetadataRefresher.pinSessionId(providerSessionId);
|
|
3437
|
+
}
|
|
3438
|
+
else {
|
|
3439
|
+
updateSpecSessionIdLocal(specSessionId, 'opencode', providerSessionId, io);
|
|
3440
|
+
}
|
|
3441
|
+
opencodeSubagentMonitor?.setParentSessionId(providerSessionId);
|
|
3442
|
+
subagentTracker.setMainSessionId(providerSessionId);
|
|
3443
|
+
}
|
|
3484
3444
|
subagentTracker.handle(subagentEvents);
|
|
3485
3445
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
3486
3446
|
firstFinalAnswerTime = null;
|
|
@@ -3509,7 +3469,16 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3509
3469
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
3510
3470
|
executionCapableEventObserved = true;
|
|
3511
3471
|
}
|
|
3512
|
-
|
|
3472
|
+
if (providerSessionId) {
|
|
3473
|
+
if (opencodeMetadataRefresher) {
|
|
3474
|
+
opencodeMetadataRefresher.pinSessionId(providerSessionId);
|
|
3475
|
+
}
|
|
3476
|
+
else {
|
|
3477
|
+
updateSpecSessionIdLocal(specSessionId, 'opencode', providerSessionId, io);
|
|
3478
|
+
}
|
|
3479
|
+
opencodeSubagentMonitor?.setParentSessionId(providerSessionId);
|
|
3480
|
+
subagentTracker.setMainSessionId(providerSessionId);
|
|
3481
|
+
}
|
|
3513
3482
|
subagentTracker.handle(subagentEvents);
|
|
3514
3483
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
3515
3484
|
firstFinalAnswerTime = null;
|
|
@@ -3535,11 +3504,12 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3535
3504
|
?? resumeSessionId;
|
|
3536
3505
|
await opencodeSubagentMonitor?.refresh();
|
|
3537
3506
|
if (specState.stopRequested) {
|
|
3538
|
-
if (resolvedOpencodeSessionId) {
|
|
3507
|
+
if (resolvedOpencodeSessionId && opencodeSessionOperations) {
|
|
3539
3508
|
usageSnapshot = await resolveOpencodeUsageSnapshot({
|
|
3540
3509
|
current: usageSnapshot,
|
|
3541
3510
|
sessionId: resolvedOpencodeSessionId,
|
|
3542
3511
|
spawnTimestamp,
|
|
3512
|
+
sessionOperations: opencodeSessionOperations,
|
|
3543
3513
|
});
|
|
3544
3514
|
}
|
|
3545
3515
|
updateSpecSessionStatusLocal(specSessionId, 'running', preservedOutput?.log ?? null, io, preservedOutput?.response ?? null);
|
|
@@ -3559,11 +3529,12 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3559
3529
|
log,
|
|
3560
3530
|
response: finalResponse,
|
|
3561
3531
|
});
|
|
3562
|
-
if (resolvedOpencodeSessionId && spawnTimestamp !== null) {
|
|
3532
|
+
if (resolvedOpencodeSessionId && spawnTimestamp !== null && opencodeSessionOperations) {
|
|
3563
3533
|
usageSnapshot = await resolveOpencodeUsageSnapshot({
|
|
3564
3534
|
current: usageSnapshot,
|
|
3565
3535
|
sessionId: resolvedOpencodeSessionId,
|
|
3566
3536
|
spawnTimestamp,
|
|
3537
|
+
sessionOperations: opencodeSessionOperations,
|
|
3567
3538
|
});
|
|
3568
3539
|
}
|
|
3569
3540
|
updateSpecSessionStatusLocal(specSessionId, 'running', finalOutput.log, io, finalOutput.response);
|
|
@@ -3637,7 +3608,7 @@ export const runSpecAgent = (options) => {
|
|
|
3637
3608
|
* updateSpecReviewRunStatus / updateSpecReviewRunStreaming / updateSpecReviewRunUsage —
|
|
3638
3609
|
* the spec_sessions row is never touched by this path.
|
|
3639
3610
|
*/
|
|
3640
|
-
const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], executionContext, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
3611
|
+
const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, skills = [], executionContext, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), opencodeRuntimeIdentity = null, }) => {
|
|
3641
3612
|
const registeredState = activeSpecReviewProcesses.get(specReviewRunId);
|
|
3642
3613
|
if (registeredState?.stopRequested) {
|
|
3643
3614
|
return Promise.resolve({
|
|
@@ -3648,11 +3619,15 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3648
3619
|
});
|
|
3649
3620
|
}
|
|
3650
3621
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
3622
|
+
const resolvedOpencodeCompatibility = resolveOpencodeCompatibility(tool, executionProvider, opencodeRuntimeIdentity);
|
|
3623
|
+
const resolvedOpencodeRuntimeIdentity = resolvedOpencodeCompatibility?.runtimeIdentity ?? null;
|
|
3624
|
+
const opencodeSessionOperations = executionProvider.sessionOperations?.opencode ?? null;
|
|
3651
3625
|
const subagentTracker = createSubagentRunTracker({
|
|
3652
3626
|
parentType: 'spec',
|
|
3653
3627
|
parentId: specSessionId,
|
|
3654
3628
|
io,
|
|
3655
3629
|
executionContext,
|
|
3630
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
3656
3631
|
});
|
|
3657
3632
|
return new Promise((resolve, reject) => {
|
|
3658
3633
|
let spawnTimestamp = null;
|
|
@@ -3696,9 +3671,18 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3696
3671
|
mode: 'plan',
|
|
3697
3672
|
resumeSessionId: null,
|
|
3698
3673
|
sessionId: randomUUID(),
|
|
3674
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
3675
|
+
opencodeCompatibility: resolvedOpencodeCompatibility,
|
|
3699
3676
|
};
|
|
3700
3677
|
const invocation = tool === 'opencode'
|
|
3701
|
-
? buildOpencodeSpecReviewInvocation({
|
|
3678
|
+
? buildOpencodeSpecReviewInvocation({
|
|
3679
|
+
prompt,
|
|
3680
|
+
files,
|
|
3681
|
+
model: resolvedModel,
|
|
3682
|
+
variant,
|
|
3683
|
+
opencodeRuntimeIdentity: resolvedOpencodeRuntimeIdentity,
|
|
3684
|
+
opencodeCompatibility: resolvedOpencodeCompatibility,
|
|
3685
|
+
})
|
|
3702
3686
|
: agentProvider.buildStreamingInvocation(streamingRequest);
|
|
3703
3687
|
try {
|
|
3704
3688
|
const handle = executionProvider.execute({
|
|
@@ -3763,6 +3747,9 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3763
3747
|
spawnTimestamp,
|
|
3764
3748
|
cwd,
|
|
3765
3749
|
tracker: subagentTracker,
|
|
3750
|
+
taskSessionReader: opencodeSessionOperations
|
|
3751
|
+
? createOpencodeTaskSessionReader(opencodeSessionOperations, { directory: cwd })
|
|
3752
|
+
: null,
|
|
3766
3753
|
parentDiscoveryDelayMs: OPENCODE_SESSION_METADATA_LOOKUP_DELAY_MS,
|
|
3767
3754
|
});
|
|
3768
3755
|
opencodeSubagentMonitor.start();
|
|
@@ -3780,7 +3767,7 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3780
3767
|
const lines = text.split('\n');
|
|
3781
3768
|
remainder = lines.pop() ?? '';
|
|
3782
3769
|
for (const line of lines) {
|
|
3783
|
-
usageSnapshot =
|
|
3770
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, line);
|
|
3784
3771
|
const { logFragment, responseFragment, subagentEvents } = parseClaudeStreamJsonLine(line);
|
|
3785
3772
|
subagentTracker.handle(subagentEvents);
|
|
3786
3773
|
if (logFragment !== null) {
|
|
@@ -3803,7 +3790,7 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3803
3790
|
return;
|
|
3804
3791
|
}
|
|
3805
3792
|
if (remainder.trim()) {
|
|
3806
|
-
usageSnapshot =
|
|
3793
|
+
usageSnapshot = mergeClaudeUsageFromJsonLine(usageSnapshot, remainder);
|
|
3807
3794
|
const { logFragment, responseFragment, subagentEvents } = parseClaudeStreamJsonLine(remainder);
|
|
3808
3795
|
subagentTracker.handle(subagentEvents);
|
|
3809
3796
|
if (logFragment !== null) {
|
|
@@ -3865,18 +3852,8 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3865
3852
|
for (const line of lines) {
|
|
3866
3853
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
3867
3854
|
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
try {
|
|
3871
|
-
const event = JSON.parse(trimmedLine);
|
|
3872
|
-
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
3873
|
-
const phase = event.part.metadata?.openai?.phase;
|
|
3874
|
-
if (!phase || phase === 'final_answer') {
|
|
3875
|
-
firstFinalAnswerTime = Date.now();
|
|
3876
|
-
}
|
|
3877
|
-
}
|
|
3878
|
-
}
|
|
3879
|
-
catch { /* best-effort timing */ }
|
|
3855
|
+
if (hasOpencodeFinalAnswer(line)) {
|
|
3856
|
+
firstFinalAnswerTime = Date.now();
|
|
3880
3857
|
}
|
|
3881
3858
|
}
|
|
3882
3859
|
const parsed = parseOpencodeJsonLine(line, cwd);
|
|
@@ -3917,11 +3894,12 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3917
3894
|
const finalResponse = recoveryTracker.hasRecoveredFinalAnswer()
|
|
3918
3895
|
? recoveredResponse
|
|
3919
3896
|
: responseLines.join('\n\n') || stderrOutput;
|
|
3920
|
-
if (opencodeSessionId && spawnTimestamp !== null) {
|
|
3897
|
+
if (opencodeSessionId && spawnTimestamp !== null && opencodeSessionOperations) {
|
|
3921
3898
|
usageSnapshot = await resolveOpencodeUsageSnapshot({
|
|
3922
3899
|
current: usageSnapshot,
|
|
3923
3900
|
sessionId: opencodeSessionId,
|
|
3924
3901
|
spawnTimestamp,
|
|
3902
|
+
sessionOperations: opencodeSessionOperations,
|
|
3925
3903
|
});
|
|
3926
3904
|
}
|
|
3927
3905
|
if (reviewState.stopRequested) {
|