@straiffi/archon 1.3.1 → 1.3.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/dist/client/assets/{TestsDialog-DelI4dOJ.js → TestsDialog-XvTa9G9O.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-Cn7GlwQg.js → architectureDiagram-3BPJPVTR-DGA6BOBZ.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-Dbt3i35a.js → blockDiagram-GPEHLZMM-DxNTme2C.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BCNsqu0u.js → c4Diagram-AAUBKEIU-BVtVwcm1.js} +1 -1
- package/dist/client/assets/channel-DesU8BSU.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-B9c8AzpT.js → chunk-2J33WTMH-kjE402ke.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-D-qpTyaE.js → chunk-3OPIFGDE-ZhKa-iUJ.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-bBV3HPCk.js → chunk-5ZQYHXKU-DD-CFUFz.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-InkxsEhY.js → chunk-727SXJPM-tYKnuL4a.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DZZL694W.js → chunk-AQP2D5EJ-DTWw4ohg.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-BuAgybUd.js → chunk-CSCIHK7Q-nqMgQFJj.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-B9fgatSq.js → chunk-KSCS5N6A-CG7nuzWr.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-L-7z2xOe.js → chunk-L5ZTLDWV-8_YeOc_Q.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-hrScZnrx.js → chunk-LZXEDZCA-Baibx6Tw.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-d2HjiRzD.js → chunk-ND2GUHAM-B6WTUT1I.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-C6cn05ZM.js → chunk-NZK2D7GU-D_9rHSdg.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-BAjbtmB_.js → chunk-O5CBEL6O-C7gZvogK.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-By4sIjXN.js → chunk-WU5MYG2G-5mFJEt4y.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BXN2p5zx.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C4KSVWqM.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-_iu0KXos.js → dagre-BM42HDAG-C4bt9HKe.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DyBAakP6.js → diagram-2AECGRRQ-ChyO8KAx.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-xD8YOWp-.js → diagram-5GNKFQAL-Pv5v9f8J.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BQDiusQc.js → diagram-KO2AKTUF-D01uZAEx.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-BRu1DXcK.js → diagram-LMA3HP47-SgsZHK1n.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C3A1wt8S.js → diagram-OG6HWLK6-NoXWNvb9.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-C_fWil4q.js → erDiagram-TEJ5UH35-CILc_dlJ.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-CKsgXWad.js → flowDiagram-I6XJVG4X-DrPbsQ2p.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-DRhmLmdi.js → ganttDiagram-6RSMTGT7-CZJImvrO.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-C34Vr5C1.js → gitGraphDiagram-PVQCEYII-DOV6sM0r.js} +1 -1
- package/dist/client/assets/index-BA7rzT42.css +2 -0
- package/dist/client/assets/{index-DENrWubs.js → index-BxKx-nzY.js} +102 -102
- package/dist/client/assets/{infoDiagram-5YYISTIA-Bkbr_hQH.js → infoDiagram-5YYISTIA-CrlDAG9K.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CEXsy2hU.js → ishikawaDiagram-YF4QCWOH-BlzVoHRp.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-RuKnL83f.js → journeyDiagram-JHISSGLW-CzCVPcHP.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DrlhCmkA.js → kanban-definition-UN3LZRKU-BL53KDiy.js} +1 -1
- package/dist/client/assets/{line-CGdxyPh9.js → line-BVzc-l73.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CbzywbCW.js → mermaid-parser.core-Bex-oQWq.js} +1 -1
- package/dist/client/assets/{mermaid.core-09FBpi2R.js → mermaid.core-BtD6cM5K.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CXrsI5b1.js → mindmap-definition-RKZ34NQL-jl3kudNu.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-HbOpep9N.js → pieDiagram-4H26LBE5-CH992aFg.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BWw-ZWMn.js → quadrantDiagram-W4KKPZXB-CzXkWlOp.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-CpscsQdI.js → requirementDiagram-4Y6WPE33-DJg-bHHs.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BoV5tyAf.js → sankeyDiagram-5OEKKPKP-DitY__t-.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CBcDRtjR.js → sequenceDiagram-3UESZ5HK-DRPu-NkL.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-Df9lcs70.js → stateDiagram-AJRCARHV--Q4rPa1d.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D7AQwkBW.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BFv1VICg.js → timeline-definition-PNZ67QCA-CEWh_YRb.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DPRtn-XH.js → vennDiagram-CIIHVFJN-CeH6-kWE.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CDiint0P.js → wardleyDiagram-YWT4CUSO-DUHb2UPZ.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-ELyFdgmG.js → xychartDiagram-2RQKCTM6-DP2fabhc.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +8 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +11 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +110 -20
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/chatMessages.js +6 -6
- package/dist/server/lib/chatMessages.js.map +1 -1
- package/dist/server/lib/chatOperations.js +13 -2
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/chats.js +32 -4
- package/dist/server/lib/chats.js.map +1 -1
- package/dist/server/lib/specArtifact.js +17 -0
- package/dist/server/lib/specArtifact.js.map +1 -1
- package/dist/server/lib/specDraft.js +19 -0
- package/dist/server/lib/specDraft.js.map +1 -1
- package/dist/server/lib/specMessages.js +8 -6
- package/dist/server/lib/specMessages.js.map +1 -1
- package/dist/server/lib/ticketBatch.js +1 -0
- package/dist/server/lib/ticketBatch.js.map +1 -1
- package/dist/server/lib/ticketMessages.js +6 -6
- package/dist/server/lib/ticketMessages.js.map +1 -1
- package/dist/server/workers/build.js +2 -1
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +2 -0
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +2 -1
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +34 -6
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +3 -2
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +2 -1
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +27 -3
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-BmkYY3c1.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-B9B9lIAo.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DXR72HYe.js +0 -1
- package/dist/client/assets/index-iElL-crz.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-FIWIctC4.js +0 -1
package/dist/server/lib/agent.js
CHANGED
|
@@ -182,8 +182,13 @@ const getErrorMessage = (error) => {
|
|
|
182
182
|
};
|
|
183
183
|
export const formatAgentErrorForTranscript = (error) => {
|
|
184
184
|
const rawMessage = getErrorMessage(error);
|
|
185
|
-
const
|
|
186
|
-
|
|
185
|
+
const prefixMatch = rawMessage.match(/^(Agent exited with code \S+)\n(.*)/s);
|
|
186
|
+
if (!prefixMatch) {
|
|
187
|
+
return rawMessage;
|
|
188
|
+
}
|
|
189
|
+
const [, prefix, logContent] = prefixMatch;
|
|
190
|
+
const trimmedLog = logContent.trim();
|
|
191
|
+
return trimmedLog || prefix;
|
|
187
192
|
};
|
|
188
193
|
const getPersistedChatState = (chatSessionId) => {
|
|
189
194
|
const row = db.prepare(GET_PERSISTED_CHAT_STATE_QUERY).get(chatSessionId);
|
|
@@ -915,9 +920,14 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
915
920
|
};
|
|
916
921
|
}
|
|
917
922
|
if (event.type === 'error') {
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
923
|
+
const errorText = typeof event.error === 'string'
|
|
924
|
+
? event.error.trim()
|
|
925
|
+
: event.error && typeof event.error === 'object'
|
|
926
|
+
? (event.error.data?.message?.trim()
|
|
927
|
+
|| event.error.message?.trim()
|
|
928
|
+
|| JSON.stringify(event.error))
|
|
929
|
+
: '';
|
|
930
|
+
const text = event.part?.text?.trim() || errorText || '';
|
|
921
931
|
if (text) {
|
|
922
932
|
return {
|
|
923
933
|
logFragment: `**Error:** ${text}`,
|
|
@@ -968,6 +978,18 @@ const parseOpencodeJsonLine = (line, cwd) => {
|
|
|
968
978
|
}
|
|
969
979
|
return { logFragment: null, responseFragment: null };
|
|
970
980
|
};
|
|
981
|
+
const buildResponseTiming = ({ startTime, firstFinalAnswerTime, isStopped, }) => {
|
|
982
|
+
if (!startTime) {
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
const totalMs = Date.now() - startTime;
|
|
986
|
+
if (isStopped || firstFinalAnswerTime === null) {
|
|
987
|
+
return { totalMs };
|
|
988
|
+
}
|
|
989
|
+
const thinkingMs = firstFinalAnswerTime - startTime;
|
|
990
|
+
const responseMs = Date.now() - firstFinalAnswerTime;
|
|
991
|
+
return { totalMs, thinkingMs, responseMs };
|
|
992
|
+
};
|
|
971
993
|
const isModelResolutionError = (message) => {
|
|
972
994
|
if (typeof message !== 'string') {
|
|
973
995
|
return false;
|
|
@@ -1095,6 +1117,8 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1095
1117
|
let sessionLookupTimer = null;
|
|
1096
1118
|
let hasResolvedOpencodeSessionId = Boolean(resumeSessionId);
|
|
1097
1119
|
let settled = false;
|
|
1120
|
+
let startTime = null;
|
|
1121
|
+
let firstFinalAnswerTime = null;
|
|
1098
1122
|
const buildState = trackBuildProcess
|
|
1099
1123
|
? { child: null, stopRequested: false, sessionLookupTimer: null }
|
|
1100
1124
|
: null;
|
|
@@ -1161,7 +1185,7 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1161
1185
|
]
|
|
1162
1186
|
];
|
|
1163
1187
|
})()
|
|
1164
|
-
: ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json',
|
|
1188
|
+
: ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), prompt]];
|
|
1165
1189
|
updateStatus(ticketId, 'running', null, io);
|
|
1166
1190
|
if (tool === 'claude' && sessionId) {
|
|
1167
1191
|
updateSessionId(ticketId, sessionId, sessionField, io);
|
|
@@ -1179,6 +1203,7 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1179
1203
|
buildState.child = child;
|
|
1180
1204
|
activeBuildProcesses.set(ticketId, buildState);
|
|
1181
1205
|
}
|
|
1206
|
+
startTime = Date.now();
|
|
1182
1207
|
if (tool === 'claude') {
|
|
1183
1208
|
const logLines = [];
|
|
1184
1209
|
const responseLines = [];
|
|
@@ -1220,24 +1245,25 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1220
1245
|
const finalLog = logLines.join('\n\n') || errOutput;
|
|
1221
1246
|
const finalResponse = responseLines.join('\n\n') || errOutput;
|
|
1222
1247
|
const modelError = isModelResolutionError(finalLog);
|
|
1248
|
+
const timing = startTime ? { totalMs: Date.now() - startTime } : null;
|
|
1223
1249
|
if (buildState?.stopRequested) {
|
|
1224
1250
|
updateStatus(ticketId, 'stopped', finalLog, io, finalResponse);
|
|
1225
|
-
resolveOnce(finalResponse);
|
|
1251
|
+
resolveOnce({ response: finalResponse, responseTiming: timing });
|
|
1226
1252
|
return;
|
|
1227
1253
|
}
|
|
1228
1254
|
if (code === 0 && !modelError) {
|
|
1229
1255
|
updateStatus(ticketId, 'done', finalLog, io, finalResponse, { emit: emitSuccessUpdate });
|
|
1230
|
-
resolveOnce(finalResponse);
|
|
1256
|
+
resolveOnce({ response: finalResponse, responseTiming: timing });
|
|
1231
1257
|
return;
|
|
1232
1258
|
}
|
|
1233
1259
|
updateStatus(ticketId, 'error', finalLog + (errOutput ? '\n\n' + errOutput : ''), io, finalResponse);
|
|
1234
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}`));
|
|
1260
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}${errOutput !== finalLog && errOutput ? '\n\n' + errOutput : ''}`));
|
|
1235
1261
|
});
|
|
1236
1262
|
child.on('error', err => {
|
|
1237
1263
|
progressUpdater.cancel();
|
|
1238
1264
|
if (buildState?.stopRequested) {
|
|
1239
1265
|
updateStatus(ticketId, 'stopped', null, io);
|
|
1240
|
-
resolveOnce(null);
|
|
1266
|
+
resolveOnce({ response: null, responseTiming: null });
|
|
1241
1267
|
return;
|
|
1242
1268
|
}
|
|
1243
1269
|
updateStatus(ticketId, 'error', getErrorMessage(err), io);
|
|
@@ -1257,6 +1283,21 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1257
1283
|
const lines = text.split('\n');
|
|
1258
1284
|
remainder = lines.pop() ?? '';
|
|
1259
1285
|
for (const line of lines) {
|
|
1286
|
+
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
1287
|
+
const trimmedLine = line.trim();
|
|
1288
|
+
if (trimmedLine.startsWith('{')) {
|
|
1289
|
+
try {
|
|
1290
|
+
const event = JSON.parse(trimmedLine);
|
|
1291
|
+
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
1292
|
+
const phase = event.part.metadata?.openai?.phase;
|
|
1293
|
+
if (!phase || phase === 'final_answer') {
|
|
1294
|
+
firstFinalAnswerTime = Date.now();
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
catch { /* best-effort timing */ }
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1260
1301
|
const { logFragment, responseFragment } = parseOpencodeJsonLine(line);
|
|
1261
1302
|
if (logFragment !== null) {
|
|
1262
1303
|
logLines.push(logFragment);
|
|
@@ -1299,18 +1340,19 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1299
1340
|
const log = logLines.join('\n\n') || stderrOutput;
|
|
1300
1341
|
const finalResponse = responseLines.join('\n\n') || stderrOutput;
|
|
1301
1342
|
const modelError = isModelResolutionError(log);
|
|
1343
|
+
const timing = buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: Boolean(buildState?.stopRequested) });
|
|
1302
1344
|
if (buildState?.stopRequested) {
|
|
1303
1345
|
updateStatus(ticketId, 'stopped', log, io, finalResponse);
|
|
1304
|
-
resolveOnce(finalResponse);
|
|
1346
|
+
resolveOnce({ response: finalResponse, responseTiming: timing });
|
|
1305
1347
|
return;
|
|
1306
1348
|
}
|
|
1307
1349
|
if (code === 0 && !modelError) {
|
|
1308
1350
|
updateStatus(ticketId, 'done', log, io, finalResponse, { emit: emitSuccessUpdate });
|
|
1309
|
-
resolveOnce(finalResponse);
|
|
1351
|
+
resolveOnce({ response: finalResponse, responseTiming: timing });
|
|
1310
1352
|
return;
|
|
1311
1353
|
}
|
|
1312
1354
|
updateStatus(ticketId, 'error', log, io, finalResponse);
|
|
1313
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}`));
|
|
1355
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}${stderrOutput !== log && stderrOutput ? '\n\n' + stderrOutput : ''}`));
|
|
1314
1356
|
});
|
|
1315
1357
|
child.on('error', err => {
|
|
1316
1358
|
progressUpdater.cancel();
|
|
@@ -1320,7 +1362,7 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
|
|
|
1320
1362
|
}
|
|
1321
1363
|
if (buildState?.stopRequested) {
|
|
1322
1364
|
updateStatus(ticketId, 'stopped', null, io);
|
|
1323
|
-
resolveOnce(null);
|
|
1365
|
+
resolveOnce({ response: null, responseTiming: null });
|
|
1324
1366
|
return;
|
|
1325
1367
|
}
|
|
1326
1368
|
updateStatus(ticketId, 'error', getErrorMessage(err), io);
|
|
@@ -1335,6 +1377,8 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1335
1377
|
let spawnTimestamp = null;
|
|
1336
1378
|
let opencodeMetadataRefresher = null;
|
|
1337
1379
|
let settled = false;
|
|
1380
|
+
let startTime = null;
|
|
1381
|
+
let firstFinalAnswerTime = null;
|
|
1338
1382
|
let usageSnapshot = EMPTY_CHAT_USAGE_SNAPSHOT;
|
|
1339
1383
|
const chatState = {
|
|
1340
1384
|
child: null,
|
|
@@ -1383,7 +1427,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1383
1427
|
]
|
|
1384
1428
|
];
|
|
1385
1429
|
})()
|
|
1386
|
-
: ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json',
|
|
1430
|
+
: ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
|
|
1387
1431
|
updateChatSessionStatus(chatSessionId, 'running', null, io);
|
|
1388
1432
|
if (tool === 'claude' && sessionId) {
|
|
1389
1433
|
updateChatSessionId(chatSessionId, tool, sessionId, io);
|
|
@@ -1412,6 +1456,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1412
1456
|
});
|
|
1413
1457
|
chatState.child = child;
|
|
1414
1458
|
activeChatProcesses.set(chatSessionId, chatState);
|
|
1459
|
+
startTime = Date.now();
|
|
1415
1460
|
if (tool === 'claude') {
|
|
1416
1461
|
const logLines = [];
|
|
1417
1462
|
const responseLines = [];
|
|
@@ -1465,6 +1510,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1465
1510
|
completionStatus: 'stopped',
|
|
1466
1511
|
finalResponse: preservedOutput.response,
|
|
1467
1512
|
usageSnapshot,
|
|
1513
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1468
1514
|
});
|
|
1469
1515
|
return;
|
|
1470
1516
|
}
|
|
@@ -1480,11 +1526,12 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1480
1526
|
completionStatus: 'done',
|
|
1481
1527
|
finalResponse: finalOutput.response,
|
|
1482
1528
|
usageSnapshot,
|
|
1529
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: false }),
|
|
1483
1530
|
});
|
|
1484
1531
|
return;
|
|
1485
1532
|
}
|
|
1486
1533
|
updateChatSessionStatus(chatSessionId, 'error', finalLog + (errOutput ? '\n\n' + errOutput : ''), io, finalResponse);
|
|
1487
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}`));
|
|
1534
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}${errOutput !== finalLog && errOutput ? '\n\n' + errOutput : ''}`));
|
|
1488
1535
|
});
|
|
1489
1536
|
child.on('error', err => {
|
|
1490
1537
|
progressUpdater.cancel();
|
|
@@ -1501,6 +1548,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1501
1548
|
completionStatus: 'stopped',
|
|
1502
1549
|
finalResponse: preservedOutput.response,
|
|
1503
1550
|
usageSnapshot,
|
|
1551
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1504
1552
|
});
|
|
1505
1553
|
return;
|
|
1506
1554
|
}
|
|
@@ -1522,6 +1570,21 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1522
1570
|
remainder = lines.pop() ?? '';
|
|
1523
1571
|
for (const line of lines) {
|
|
1524
1572
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
1573
|
+
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
1574
|
+
const trimmedLine = line.trim();
|
|
1575
|
+
if (trimmedLine.startsWith('{')) {
|
|
1576
|
+
try {
|
|
1577
|
+
const event = JSON.parse(trimmedLine);
|
|
1578
|
+
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
1579
|
+
const phase = event.part.metadata?.openai?.phase;
|
|
1580
|
+
if (!phase || phase === 'final_answer') {
|
|
1581
|
+
firstFinalAnswerTime = Date.now();
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
catch { /* best-effort timing */ }
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1525
1588
|
const { logFragment, responseFragment } = parseOpencodeJsonLine(line, cwd);
|
|
1526
1589
|
if (logFragment !== null) {
|
|
1527
1590
|
logLines.push(logFragment);
|
|
@@ -1578,6 +1641,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1578
1641
|
completionStatus: 'stopped',
|
|
1579
1642
|
finalResponse: preservedOutput?.response ?? null,
|
|
1580
1643
|
usageSnapshot,
|
|
1644
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1581
1645
|
});
|
|
1582
1646
|
return;
|
|
1583
1647
|
}
|
|
@@ -1600,11 +1664,12 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1600
1664
|
completionStatus: 'done',
|
|
1601
1665
|
finalResponse: finalOutput.response,
|
|
1602
1666
|
usageSnapshot,
|
|
1667
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: false }),
|
|
1603
1668
|
});
|
|
1604
1669
|
return;
|
|
1605
1670
|
}
|
|
1606
1671
|
updateChatSessionStatus(chatSessionId, 'error', log, io, finalResponse);
|
|
1607
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}`));
|
|
1672
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}${stderrOutput !== log && stderrOutput ? '\n\n' + stderrOutput : ''}`));
|
|
1608
1673
|
});
|
|
1609
1674
|
child.on('error', err => {
|
|
1610
1675
|
progressUpdater.cancel();
|
|
@@ -1621,6 +1686,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1621
1686
|
completionStatus: 'stopped',
|
|
1622
1687
|
finalResponse: preservedOutput.response,
|
|
1623
1688
|
usageSnapshot,
|
|
1689
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1624
1690
|
});
|
|
1625
1691
|
return;
|
|
1626
1692
|
}
|
|
@@ -1636,6 +1702,8 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1636
1702
|
let spawnTimestamp = null;
|
|
1637
1703
|
let opencodeMetadataRefresher = null;
|
|
1638
1704
|
let settled = false;
|
|
1705
|
+
let startTime = null;
|
|
1706
|
+
let firstFinalAnswerTime = null;
|
|
1639
1707
|
let usageSnapshot = EMPTY_CHAT_USAGE_SNAPSHOT;
|
|
1640
1708
|
const specState = {
|
|
1641
1709
|
child: null,
|
|
@@ -1673,7 +1741,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1673
1741
|
],
|
|
1674
1742
|
];
|
|
1675
1743
|
})()
|
|
1676
|
-
: ['opencode', ['run', '--agent', 'plan', '--format', 'json', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
|
|
1744
|
+
: ['opencode', ['run', '--agent', 'plan', '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
|
|
1677
1745
|
updateSpecSessionStatusLocal(specSessionId, 'running', null, io);
|
|
1678
1746
|
if (tool === 'claude' && sessionId) {
|
|
1679
1747
|
updateSpecSessionIdLocal(specSessionId, tool, sessionId, io);
|
|
@@ -1702,6 +1770,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1702
1770
|
});
|
|
1703
1771
|
specState.child = child;
|
|
1704
1772
|
activeSpecProcesses.set(specSessionId, specState);
|
|
1773
|
+
startTime = Date.now();
|
|
1705
1774
|
if (tool === 'claude') {
|
|
1706
1775
|
const logLines = [];
|
|
1707
1776
|
const responseLines = [];
|
|
@@ -1755,6 +1824,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1755
1824
|
completionStatus: 'stopped',
|
|
1756
1825
|
finalResponse: preservedOutput.response,
|
|
1757
1826
|
usageSnapshot,
|
|
1827
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1758
1828
|
});
|
|
1759
1829
|
return;
|
|
1760
1830
|
}
|
|
@@ -1770,11 +1840,12 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1770
1840
|
completionStatus: 'done',
|
|
1771
1841
|
finalResponse: finalOutput.response,
|
|
1772
1842
|
usageSnapshot,
|
|
1843
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: false }),
|
|
1773
1844
|
});
|
|
1774
1845
|
return;
|
|
1775
1846
|
}
|
|
1776
1847
|
updateSpecSessionStatusLocal(specSessionId, 'error', finalLog + (errOutput ? '\n\n' + errOutput : ''), io, finalResponse);
|
|
1777
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}`));
|
|
1848
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}${errOutput !== finalLog && errOutput ? '\n\n' + errOutput : ''}`));
|
|
1778
1849
|
});
|
|
1779
1850
|
child.on('error', err => {
|
|
1780
1851
|
progressUpdater.cancel();
|
|
@@ -1791,6 +1862,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1791
1862
|
completionStatus: 'stopped',
|
|
1792
1863
|
finalResponse: preservedOutput.response,
|
|
1793
1864
|
usageSnapshot,
|
|
1865
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1794
1866
|
});
|
|
1795
1867
|
return;
|
|
1796
1868
|
}
|
|
@@ -1812,6 +1884,21 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1812
1884
|
remainder = lines.pop() ?? '';
|
|
1813
1885
|
for (const line of lines) {
|
|
1814
1886
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
1887
|
+
if (firstFinalAnswerTime === null && startTime !== null) {
|
|
1888
|
+
const trimmedLine = line.trim();
|
|
1889
|
+
if (trimmedLine.startsWith('{')) {
|
|
1890
|
+
try {
|
|
1891
|
+
const event = JSON.parse(trimmedLine);
|
|
1892
|
+
if (event.type === 'text' && event.part?.type === 'text' && event.part.text) {
|
|
1893
|
+
const phase = event.part.metadata?.openai?.phase;
|
|
1894
|
+
if (!phase || phase === 'final_answer') {
|
|
1895
|
+
firstFinalAnswerTime = Date.now();
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
catch { /* best-effort timing */ }
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1815
1902
|
const { logFragment, responseFragment } = parseOpencodeJsonLine(line, cwd);
|
|
1816
1903
|
if (logFragment !== null) {
|
|
1817
1904
|
logLines.push(logFragment);
|
|
@@ -1868,6 +1955,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1868
1955
|
completionStatus: 'stopped',
|
|
1869
1956
|
finalResponse: preservedOutput?.response ?? null,
|
|
1870
1957
|
usageSnapshot,
|
|
1958
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1871
1959
|
});
|
|
1872
1960
|
return;
|
|
1873
1961
|
}
|
|
@@ -1890,11 +1978,12 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1890
1978
|
completionStatus: 'done',
|
|
1891
1979
|
finalResponse: finalOutput.response,
|
|
1892
1980
|
usageSnapshot,
|
|
1981
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: false }),
|
|
1893
1982
|
});
|
|
1894
1983
|
return;
|
|
1895
1984
|
}
|
|
1896
1985
|
updateSpecSessionStatusLocal(specSessionId, 'error', log, io, finalResponse);
|
|
1897
|
-
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}`));
|
|
1986
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}${stderrOutput !== log && stderrOutput ? '\n\n' + stderrOutput : ''}`));
|
|
1898
1987
|
});
|
|
1899
1988
|
child.on('error', err => {
|
|
1900
1989
|
progressUpdater.cancel();
|
|
@@ -1911,6 +2000,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1911
2000
|
completionStatus: 'stopped',
|
|
1912
2001
|
finalResponse: preservedOutput.response,
|
|
1913
2002
|
usageSnapshot,
|
|
2003
|
+
responseTiming: buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: true }),
|
|
1914
2004
|
});
|
|
1915
2005
|
return;
|
|
1916
2006
|
}
|