@straiffi/archon 1.2.33 → 1.3.0
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-j0fYZ2AP.js +5 -0
- package/dist/client/assets/arc-4YfDy8uK.js +1 -0
- package/dist/client/assets/architecture-7EHR7CIX-B0ldQ5d_.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-CnivPz9K.js +36 -0
- package/dist/client/assets/array-DOVTz2Mq.js +1 -0
- package/dist/client/assets/blockDiagram-GPEHLZMM-Rd0YcyFR.js +132 -0
- package/dist/client/assets/c4Diagram-AAUBKEIU-kFDNpO2v.js +10 -0
- package/dist/client/assets/channel-C2eURzdr.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-CaqozdP8.js +1 -0
- package/dist/client/assets/chunk-3OPIFGDE-B8iFdxrR.js +62 -0
- package/dist/client/assets/chunk-4BX2VUAB-B3mVWXS4.js +1 -0
- package/dist/client/assets/chunk-4EGX6M5U-DTB6Um9X.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-BB-0ajVZ.js +1 -0
- package/dist/client/assets/chunk-5DO6E6H7-DtX6GYFY.js +1 -0
- package/dist/client/assets/chunk-5ZQYHXKU-BrIMEXNE.js +2 -0
- package/dist/client/assets/chunk-727SXJPM-BtIA7PWh.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-fZoHENDl.js +231 -0
- package/dist/client/assets/chunk-BR22UD5L-CkJbcXZG.js +1 -0
- package/dist/client/assets/chunk-BSJP7CBP-X4eyoSqE.js +1 -0
- package/dist/client/assets/chunk-CSCIHK7Q-DWO1NEfx.js +124 -0
- package/dist/client/assets/chunk-FHYWG6QK-Cb5GzaCb.js +1 -0
- package/dist/client/assets/chunk-FMBD7UC4-mGY2D2YB.js +15 -0
- package/dist/client/assets/chunk-KSCS5N6A-CF2IF83X.js +10 -0
- package/dist/client/assets/chunk-L5ZTLDWV-BUItv2Ib.js +1 -0
- package/dist/client/assets/chunk-LZXEDZCA-BXfHhDLH.js +2 -0
- package/dist/client/assets/chunk-MPE355IW-WT7jTCZh.js +1 -0
- package/dist/client/assets/chunk-MZUSXYTE-CClXfOSj.js +1 -0
- package/dist/client/assets/chunk-N66VUXT2-Bpe4yprw.js +1 -0
- package/dist/client/assets/chunk-ND2GUHAM-C2qj1a3s.js +1 -0
- package/dist/client/assets/chunk-NNHCCRGN-C4ci-Gd6.js +159 -0
- package/dist/client/assets/chunk-NZK2D7GU-Chir8aJ-.js +1 -0
- package/dist/client/assets/chunk-O5CBEL6O-DEfSochA.js +70 -0
- package/dist/client/assets/chunk-PUPMXCY4-Dh45WL5s.js +1 -0
- package/dist/client/assets/chunk-QZHKN3VN-U_l6-b5-.js +1 -0
- package/dist/client/assets/chunk-UIBZB4QT-Cp2xSKlH.js +1 -0
- package/dist/client/assets/chunk-WCWK7LTN-BOt5FFIu.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-BszlpZ1y.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-YiT9fJFY.js +32 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-D8cVFEdr.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BJah23qn.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-CtC2zeMr.js +1 -0
- package/dist/client/assets/cytoscape.esm-DnmRtSj9.js +321 -0
- package/dist/client/assets/dagre-5AiD43Uu.js +1 -0
- package/dist/client/assets/dagre-BM42HDAG-D-czLNXn.js +4 -0
- package/dist/client/assets/defaultLocale-Ck2Xxk-C.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-DswYVROV.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-C2LB4-Kp.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-4pdiiwxL.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-DgGvwfLE.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-ODytJwCx.js +24 -0
- package/dist/client/assets/dist-BQjeO--f.js +1 -0
- package/dist/client/assets/erDiagram-TEJ5UH35-CE_PuRb6.js +85 -0
- package/dist/client/assets/eventmodeling-FCH6USID-BczyucFk.js +1 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-COhcCfz4.js +162 -0
- package/dist/client/assets/ganttDiagram-6RSMTGT7-Dj2Sy2Gh.js +292 -0
- package/dist/client/assets/gitGraph-WXDBUCRP-x5mlGyzr.js +1 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-D_3GyFuB.js +106 -0
- package/dist/client/assets/graphlib-CMVU3iJd.js +1 -0
- package/dist/client/assets/index-C4OZfq84.js +260 -0
- package/dist/client/assets/index-I0a6lbgt.css +2 -0
- package/dist/client/assets/info-J43DQDTF-BsM_Qb70.js +1 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-CBWV9QV_.js +2 -0
- package/dist/client/assets/init-Bft5Ffpj.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-Ddnuid12.js +70 -0
- package/dist/client/assets/journeyDiagram-JHISSGLW-BpXuCD5H.js +139 -0
- package/dist/client/assets/kanban-definition-UN3LZRKU-BuZe6GDK.js +89 -0
- package/dist/client/assets/katex-Bpz5kWH9.js +257 -0
- package/dist/client/assets/line-BE4yoTuW.js +1 -0
- package/dist/client/assets/linear-BYVhmsFj.js +1 -0
- package/dist/client/assets/mermaid-parser.core-B2H_eqyg.js +4 -0
- package/dist/client/assets/mermaid.core-CU1B0VWF.js +9 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-CG4XJeRx.js +96 -0
- package/dist/client/assets/ordinal-DIg8h6NI.js +1 -0
- package/dist/client/assets/packet-YPE3B663-DeVIMHV5.js +1 -0
- package/dist/client/assets/path-DfRbCp9y.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-ByRF4_cj.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-CN5uHxOy.js +30 -0
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-Bxov0SvH.js +7 -0
- package/dist/client/assets/radar-GUYGQ44K-XEreg8aR.js +1 -0
- package/dist/client/assets/requirementDiagram-4Y6WPE33-DWw4nY3R.js +84 -0
- package/dist/client/assets/rough.esm-DCvqWubr.js +1 -0
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-CzRuy9FU.js +40 -0
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-BLAdf9cH.js +162 -0
- package/dist/client/assets/src-BcaL9nCM.js +1 -0
- package/dist/client/assets/stateDiagram-AJRCARHV-Dbwp6kCy.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BWC0_Q2v.js +1 -0
- package/dist/client/assets/timeline-definition-PNZ67QCA-HajxAhbq.js +120 -0
- package/dist/client/assets/treeView-BLDUP644-D85P2Ji3.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-BnXG5utQ.js +1 -0
- package/dist/client/assets/vennDiagram-CIIHVFJN-DWaZcpRa.js +34 -0
- package/dist/client/assets/wardley-L42UT6IY-Ct9qHtJP.js +1 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-BPhQM7CW.js +78 -0
- package/dist/client/assets/xychartDiagram-2RQKCTM6-CITmODsS.js +7 -0
- package/dist/client/index.html +3 -4
- package/dist/server/db.js +91 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +384 -123
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +475 -1
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/attachments.js +146 -0
- package/dist/server/lib/attachments.js.map +1 -1
- package/dist/server/lib/buildFlow.js +2 -1
- package/dist/server/lib/buildFlow.js.map +1 -1
- package/dist/server/lib/chatOperations.js +2 -0
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/chats.js +9 -4
- package/dist/server/lib/chats.js.map +1 -1
- package/dist/server/lib/externalSource.js +32 -0
- package/dist/server/lib/externalSource.js.map +1 -0
- package/dist/server/lib/httpError.js +9 -0
- package/dist/server/lib/httpError.js.map +1 -0
- package/dist/server/lib/mobileAccess.js +3 -0
- package/dist/server/lib/mobileAccess.js.map +1 -1
- package/dist/server/lib/specArtifact.js +256 -0
- package/dist/server/lib/specArtifact.js.map +1 -0
- package/dist/server/lib/specDraft.js +33 -0
- package/dist/server/lib/specDraft.js.map +1 -0
- package/dist/server/lib/specExport.js +70 -0
- package/dist/server/lib/specExport.js.map +1 -0
- package/dist/server/lib/specFormatting.js +26 -0
- package/dist/server/lib/specFormatting.js.map +1 -0
- package/dist/server/lib/specMessages.js +47 -0
- package/dist/server/lib/specMessages.js.map +1 -0
- package/dist/server/lib/specOperations.js +150 -0
- package/dist/server/lib/specOperations.js.map +1 -0
- package/dist/server/lib/specSections.js +82 -0
- package/dist/server/lib/specSections.js.map +1 -0
- package/dist/server/lib/specs.js +318 -0
- package/dist/server/lib/specs.js.map +1 -0
- package/dist/server/lib/thinkingState.js +26 -0
- package/dist/server/lib/thinkingState.js.map +1 -1
- package/dist/server/lib/ticketBatch.js +55 -0
- package/dist/server/lib/ticketBatch.js.map +1 -0
- package/dist/server/lib/ticketDescriptionContract.js +52 -0
- package/dist/server/lib/ticketDescriptionContract.js.map +1 -0
- package/dist/server/lib/tickets.js +29 -3
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/workers/build.js +14 -1
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +10 -1
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +10 -1
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +23 -33
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +10 -0
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +14 -1
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +418 -0
- package/dist/server/workers/specSession.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/TestsDialog-B7r9ZQQo.js +0 -5
- package/dist/client/assets/dropdown-menu-BULYW006.js +0 -43
- package/dist/client/assets/index-B62IAkKy.js +0 -150
- package/dist/client/assets/index-DoNUd8CK.css +0 -2
- /package/dist/client/assets/{rolldown-runtime-BYbx6iT9.js → chunk-BNv3lrIs.js} +0 -0
package/dist/server/lib/agent.js
CHANGED
|
@@ -6,6 +6,7 @@ import db from '../db.js';
|
|
|
6
6
|
import { getChatSession, updateChatSessionUsageSnapshot } from './chats.js';
|
|
7
7
|
import { normalizeModelId } from './models.js';
|
|
8
8
|
import { PLAN_MODE_QUESTION_TOOL_NAME } from './planModeQuestions.js';
|
|
9
|
+
import { getSpecSession, updateSpecSessionResumeId, updateSpecSessionStatus, updateSpecSessionTitle, updateSpecSessionUsageSnapshot } from './specs.js';
|
|
9
10
|
import { resolveOpencodeDbPath } from './runtimePaths.js';
|
|
10
11
|
import { markTicketDescriptionVisibleInBundle } from './ticketMessages.js';
|
|
11
12
|
import { getTicket } from './tickets.js';
|
|
@@ -20,6 +21,7 @@ const OPENCODE_STEP_FINISH_USAGE_QUERY = "SELECT data FROM part WHERE session_id
|
|
|
20
21
|
const OPENCODE_STEP_FINISH_USAGE_SINCE_QUERY = "SELECT data FROM part WHERE session_id = ? AND time_created >= ? AND json_extract(data, '$.type') = 'step-finish' ORDER BY time_created DESC LIMIT 1";
|
|
21
22
|
const OPENCODE_SESSION_COST_QUERY = 'SELECT cost FROM session WHERE id = ?';
|
|
22
23
|
const GET_PERSISTED_CHAT_STATE_QUERY = 'SELECT agent_log, streaming_response, opencode_session_id FROM chat_sessions WHERE id = ?';
|
|
24
|
+
const GET_PERSISTED_SPEC_STATE_QUERY = 'SELECT agent_log, streaming_response, opencode_session_id FROM spec_sessions WHERE id = ?';
|
|
23
25
|
const EMPTY_CHAT_USAGE_SNAPSHOT = {
|
|
24
26
|
usage_snapshot_context_window: null,
|
|
25
27
|
usage_snapshot_max_output_tokens: null,
|
|
@@ -174,9 +176,15 @@ const hasMatchingUsageSnapshot = (current, next) => {
|
|
|
174
176
|
};
|
|
175
177
|
const activeBuildProcesses = new Map();
|
|
176
178
|
const activeChatProcesses = new Map();
|
|
179
|
+
const activeSpecProcesses = new Map();
|
|
177
180
|
const getErrorMessage = (error) => {
|
|
178
181
|
return error instanceof Error ? error.message : String(error);
|
|
179
182
|
};
|
|
183
|
+
export const formatAgentErrorForTranscript = (error) => {
|
|
184
|
+
const rawMessage = getErrorMessage(error);
|
|
185
|
+
const cleaned = rawMessage.replace(/^Agent exited with code \S+\n/, '').trim();
|
|
186
|
+
return cleaned || rawMessage;
|
|
187
|
+
};
|
|
180
188
|
const getPersistedChatState = (chatSessionId) => {
|
|
181
189
|
const row = db.prepare(GET_PERSISTED_CHAT_STATE_QUERY).get(chatSessionId);
|
|
182
190
|
if (!row) {
|
|
@@ -188,6 +196,17 @@ const getPersistedChatState = (chatSessionId) => {
|
|
|
188
196
|
opencodeSessionId: row.opencode_session_id ?? null,
|
|
189
197
|
};
|
|
190
198
|
};
|
|
199
|
+
const getPersistedSpecState = (specSessionId) => {
|
|
200
|
+
const row = db.prepare(GET_PERSISTED_SPEC_STATE_QUERY).get(specSessionId);
|
|
201
|
+
if (!row) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
agentLog: row.agent_log ?? null,
|
|
206
|
+
streamingResponse: row.streaming_response ?? null,
|
|
207
|
+
opencodeSessionId: row.opencode_session_id ?? null,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
191
210
|
const preferNonEmptyChatValue = (value, fallback) => {
|
|
192
211
|
if (typeof value === 'string' && value.trim()) {
|
|
193
212
|
return value;
|
|
@@ -205,6 +224,14 @@ const getPreservedChatOutput = ({ chatSessionId, log, response, }) => {
|
|
|
205
224
|
opencodeSessionId: persistedState?.opencodeSessionId ?? null,
|
|
206
225
|
};
|
|
207
226
|
};
|
|
227
|
+
const getPreservedSpecOutput = ({ specSessionId, log, response, }) => {
|
|
228
|
+
const persistedState = getPersistedSpecState(specSessionId);
|
|
229
|
+
return {
|
|
230
|
+
log: preferNonEmptyChatValue(log, persistedState?.agentLog ?? null),
|
|
231
|
+
response: preferNonEmptyChatValue(response, persistedState?.streamingResponse ?? null),
|
|
232
|
+
opencodeSessionId: persistedState?.opencodeSessionId ?? null,
|
|
233
|
+
};
|
|
234
|
+
};
|
|
208
235
|
const updateStatus = (ticketId, status, log, io, streamingResponse = null, options = {}) => {
|
|
209
236
|
db.prepare('UPDATE tickets SET agent_status = ?, agent_log = ?, streaming_response = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(status, log ?? null, streamingResponse ?? null, ticketId);
|
|
210
237
|
const updatedTicket = getTicket(ticketId);
|
|
@@ -245,6 +272,30 @@ const updateChatSessionId = (chatSessionId, tool, sessionId, io) => {
|
|
|
245
272
|
io.emit('chat-session:updated', chatSession);
|
|
246
273
|
}
|
|
247
274
|
};
|
|
275
|
+
const updateSpecSessionStatusLocal = (specSessionId, status, log, io, streamingResponse = null) => {
|
|
276
|
+
updateSpecSessionStatus(specSessionId, status, log, streamingResponse);
|
|
277
|
+
const specSession = getSpecSession(specSessionId, { includeMessages: true });
|
|
278
|
+
if (specSession) {
|
|
279
|
+
io.emit('spec-session:updated', specSession);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
const updateSpecSessionIdLocal = (specSessionId, tool, sessionId, io) => {
|
|
283
|
+
updateSpecSessionResumeId(specSessionId, tool, sessionId);
|
|
284
|
+
const specSession = getSpecSession(specSessionId, { includeMessages: true });
|
|
285
|
+
if (specSession) {
|
|
286
|
+
io.emit('spec-session:updated', specSession);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const updateSpecSessionOpencodeMetadata = (specSessionId, sessionId, title, io) => {
|
|
290
|
+
updateSpecSessionResumeId(specSessionId, 'opencode', sessionId);
|
|
291
|
+
if (title) {
|
|
292
|
+
updateSpecSessionTitle(specSessionId, title);
|
|
293
|
+
}
|
|
294
|
+
const specSession = getSpecSession(specSessionId, { includeMessages: true });
|
|
295
|
+
if (specSession) {
|
|
296
|
+
io.emit('spec-session:updated', specSession);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
248
299
|
const createThrottledProgressUpdater = (flushUpdate) => {
|
|
249
300
|
let timer = null;
|
|
250
301
|
let hasPendingUpdate = false;
|
|
@@ -300,7 +351,7 @@ const readOpencodeSession = (statement, value) => {
|
|
|
300
351
|
opencodeDb.close();
|
|
301
352
|
}
|
|
302
353
|
};
|
|
303
|
-
const readOpencodeUsageSnapshot = ({ sessionId, spawnTimestamp = null, }) => {
|
|
354
|
+
export const readOpencodeUsageSnapshot = ({ sessionId, spawnTimestamp = null, }) => {
|
|
304
355
|
const opencodeDb = openOpencodeDatabase();
|
|
305
356
|
try {
|
|
306
357
|
const costRow = opencodeDb.prepare(OPENCODE_SESSION_COST_QUERY).get(sessionId);
|
|
@@ -367,6 +418,38 @@ export const refreshChatSessionUsageSnapshot = (chatSessionId, options = {}) =>
|
|
|
367
418
|
updateChatSessionUsageSnapshot(chatSessionId, nextUsageSnapshot);
|
|
368
419
|
return getChatSession(chatSessionId, { includeMessages: options.includeMessages });
|
|
369
420
|
};
|
|
421
|
+
export const refreshSpecSessionUsageSnapshot = (specSessionId, options = {}) => {
|
|
422
|
+
const specSession = getSpecSession(specSessionId, { includeMessages: options.includeMessages });
|
|
423
|
+
if (!specSession || specSession.tool !== 'opencode' || !specSession.opencode_session_id) {
|
|
424
|
+
return specSession;
|
|
425
|
+
}
|
|
426
|
+
let nextUsageSnapshot;
|
|
427
|
+
try {
|
|
428
|
+
nextUsageSnapshot = readOpencodeUsageSnapshot({
|
|
429
|
+
sessionId: specSession.opencode_session_id,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
return specSession;
|
|
434
|
+
}
|
|
435
|
+
if (hasMatchingUsageSnapshot(specSession, nextUsageSnapshot)) {
|
|
436
|
+
return specSession;
|
|
437
|
+
}
|
|
438
|
+
updateSpecSessionUsageSnapshot(specSessionId, nextUsageSnapshot);
|
|
439
|
+
return getSpecSession(specSessionId, { includeMessages: options.includeMessages });
|
|
440
|
+
};
|
|
441
|
+
export const storeTicketUsageSnapshot = (ticketId, opencodeSessionId) => {
|
|
442
|
+
try {
|
|
443
|
+
const snapshot = readOpencodeUsageSnapshot({ sessionId: opencodeSessionId });
|
|
444
|
+
if (snapshot.usage_snapshot_total_tokens === null) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
db.prepare('UPDATE tickets SET usage_snapshot_json = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(JSON.stringify(snapshot), ticketId);
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
// Best-effort usage snapshot storage.
|
|
451
|
+
}
|
|
452
|
+
};
|
|
370
453
|
const updateChatSessionOpencodeMetadata = (chatSessionId, sessionId, title, io) => {
|
|
371
454
|
db.prepare('UPDATE chat_sessions SET opencode_session_id = ?, title = COALESCE(?, title), updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(sessionId, title, chatSessionId);
|
|
372
455
|
const chatSession = getChatSession(chatSessionId, { includeMessages: true });
|
|
@@ -442,6 +525,74 @@ const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, in
|
|
|
442
525
|
},
|
|
443
526
|
};
|
|
444
527
|
};
|
|
528
|
+
const createSpecOpencodeMetadataRefresher = ({ specSessionId, io, spawnTimestamp, initialSessionId = null, }) => {
|
|
529
|
+
let lookupTimer = null;
|
|
530
|
+
let stopped = false;
|
|
531
|
+
let knownSessionId = initialSessionId;
|
|
532
|
+
let lastAppliedSessionId = null;
|
|
533
|
+
let lastAppliedTitle = null;
|
|
534
|
+
const clearLookupTimer = () => {
|
|
535
|
+
if (!lookupTimer) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
clearTimeout(lookupTimer);
|
|
539
|
+
lookupTimer = null;
|
|
540
|
+
};
|
|
541
|
+
const readCurrentSession = () => {
|
|
542
|
+
if (knownSessionId) {
|
|
543
|
+
return readOpencodeSession(OPENCODE_SESSION_BY_ID_QUERY, knownSessionId);
|
|
544
|
+
}
|
|
545
|
+
if (spawnTimestamp === null) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
return readOpencodeSession(OPENCODE_SESSION_BY_CREATED_AT_QUERY, spawnTimestamp);
|
|
549
|
+
};
|
|
550
|
+
const refresh = () => {
|
|
551
|
+
if (stopped) {
|
|
552
|
+
return { hasSession: false, hasResolvedTitle: false, sessionId: knownSessionId };
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
const session = readCurrentSession();
|
|
556
|
+
if (!session) {
|
|
557
|
+
return { hasSession: false, hasResolvedTitle: false, sessionId: knownSessionId };
|
|
558
|
+
}
|
|
559
|
+
knownSessionId = session.id;
|
|
560
|
+
if (session.id !== lastAppliedSessionId || session.title !== lastAppliedTitle) {
|
|
561
|
+
updateSpecSessionOpencodeMetadata(specSessionId, session.id, session.title, io);
|
|
562
|
+
lastAppliedSessionId = session.id;
|
|
563
|
+
lastAppliedTitle = session.title;
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
hasSession: true,
|
|
567
|
+
hasResolvedTitle: session.title !== null,
|
|
568
|
+
sessionId: session.id,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
return { hasSession: false, hasResolvedTitle: false, sessionId: knownSessionId };
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
const scheduleRetry = () => {
|
|
576
|
+
if (stopped) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
clearLookupTimer();
|
|
580
|
+
lookupTimer = setTimeout(() => {
|
|
581
|
+
const result = refresh();
|
|
582
|
+
if (!result.hasResolvedTitle) {
|
|
583
|
+
scheduleRetry();
|
|
584
|
+
}
|
|
585
|
+
}, OPENCODE_SESSION_METADATA_LOOKUP_DELAY_MS);
|
|
586
|
+
};
|
|
587
|
+
return {
|
|
588
|
+
refresh,
|
|
589
|
+
scheduleRetry,
|
|
590
|
+
stop: () => {
|
|
591
|
+
stopped = true;
|
|
592
|
+
clearLookupTimer();
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
};
|
|
445
596
|
const TOOL_CALL_PREVIEW_MAX_LENGTH = 100;
|
|
446
597
|
const APPLY_PATCH_START_MARKER = '[[archon-apply-patch]]';
|
|
447
598
|
const APPLY_PATCH_END_MARKER = '[[/archon-apply-patch]]';
|
|
@@ -763,6 +914,17 @@ const parseOpencodeJsonEvent = (event, cwd) => {
|
|
|
763
914
|
responseFragment: null,
|
|
764
915
|
};
|
|
765
916
|
}
|
|
917
|
+
if (event.type === 'error') {
|
|
918
|
+
const text = event.part?.text?.trim()
|
|
919
|
+
|| (typeof event.error === 'string' ? event.error.trim() : '')
|
|
920
|
+
|| '';
|
|
921
|
+
if (text) {
|
|
922
|
+
return {
|
|
923
|
+
logFragment: `**Error:** ${text}`,
|
|
924
|
+
responseFragment: text,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
}
|
|
766
928
|
return { logFragment: null, responseFragment: null };
|
|
767
929
|
};
|
|
768
930
|
const parseOpencodeJsonLine = (line, cwd) => {
|
|
@@ -869,6 +1031,13 @@ const untrackChatProcess = (chatSessionId, state) => {
|
|
|
869
1031
|
state.metadataRefresher?.stop();
|
|
870
1032
|
state.metadataRefresher = null;
|
|
871
1033
|
};
|
|
1034
|
+
const untrackSpecProcess = (specSessionId, state) => {
|
|
1035
|
+
if (activeSpecProcesses.get(specSessionId) === state) {
|
|
1036
|
+
activeSpecProcesses.delete(specSessionId);
|
|
1037
|
+
}
|
|
1038
|
+
state.metadataRefresher?.stop();
|
|
1039
|
+
state.metadataRefresher = null;
|
|
1040
|
+
};
|
|
872
1041
|
export const isBuildAgentRunning = (ticketId) => activeBuildProcesses.has(ticketId);
|
|
873
1042
|
export const stopAllBuildAgents = async () => {
|
|
874
1043
|
const ticketIds = [...activeBuildProcesses.keys()];
|
|
@@ -903,6 +1072,21 @@ export const stopChatAgent = async (chatSessionId) => {
|
|
|
903
1072
|
}
|
|
904
1073
|
return true;
|
|
905
1074
|
};
|
|
1075
|
+
export const stopSpecAgent = async (specSessionId) => {
|
|
1076
|
+
const state = activeSpecProcesses.get(specSessionId);
|
|
1077
|
+
if (!state?.child) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
state.stopRequested = true;
|
|
1081
|
+
state.metadataRefresher?.stop();
|
|
1082
|
+
killProcess(state.child, 'SIGTERM');
|
|
1083
|
+
await waitForProcessExit(state.child, 5000);
|
|
1084
|
+
if (isProcessAlive(state.child)) {
|
|
1085
|
+
killProcess(state.child, 'SIGKILL');
|
|
1086
|
+
await waitForProcessExit(state.child, 1000);
|
|
1087
|
+
}
|
|
1088
|
+
return true;
|
|
1089
|
+
};
|
|
906
1090
|
export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = null, resumeSessionId = null, sessionField = 'session_id', trackBuildProcess = false, emitSuccessUpdate = true, opencodeAgentMode, }) => {
|
|
907
1091
|
const resolvedModel = normalizeModelId(model);
|
|
908
1092
|
return new Promise((resolve, reject) => {
|
|
@@ -1445,4 +1629,294 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
|
|
|
1445
1629
|
});
|
|
1446
1630
|
});
|
|
1447
1631
|
};
|
|
1632
|
+
export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, }) => {
|
|
1633
|
+
const resolvedModel = normalizeModelId(model);
|
|
1634
|
+
return new Promise((resolve, reject) => {
|
|
1635
|
+
let sessionId = null;
|
|
1636
|
+
let spawnTimestamp = null;
|
|
1637
|
+
let opencodeMetadataRefresher = null;
|
|
1638
|
+
let settled = false;
|
|
1639
|
+
let usageSnapshot = EMPTY_CHAT_USAGE_SNAPSHOT;
|
|
1640
|
+
const specState = {
|
|
1641
|
+
child: null,
|
|
1642
|
+
stopRequested: false,
|
|
1643
|
+
metadataRefresher: null,
|
|
1644
|
+
};
|
|
1645
|
+
const settle = (handler) => (value) => {
|
|
1646
|
+
if (settled) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
settled = true;
|
|
1650
|
+
opencodeMetadataRefresher?.stop();
|
|
1651
|
+
untrackSpecProcess(specSessionId, specState);
|
|
1652
|
+
handler(value);
|
|
1653
|
+
};
|
|
1654
|
+
const resolveOnce = settle(resolve);
|
|
1655
|
+
const rejectOnce = settle(reject);
|
|
1656
|
+
const [cmd, args] = tool === 'claude'
|
|
1657
|
+
? (() => {
|
|
1658
|
+
sessionId = resumeSessionId || randomUUID();
|
|
1659
|
+
return [
|
|
1660
|
+
'claude',
|
|
1661
|
+
[
|
|
1662
|
+
'--permission-mode',
|
|
1663
|
+
'plan',
|
|
1664
|
+
'--disallowedTools',
|
|
1665
|
+
PLAN_MODE_QUESTION_TOOL_NAME,
|
|
1666
|
+
'--print',
|
|
1667
|
+
'--verbose',
|
|
1668
|
+
'--output-format',
|
|
1669
|
+
'stream-json',
|
|
1670
|
+
resumeSessionId ? '--resume' : '--session-id',
|
|
1671
|
+
sessionId,
|
|
1672
|
+
prompt,
|
|
1673
|
+
],
|
|
1674
|
+
];
|
|
1675
|
+
})()
|
|
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]];
|
|
1677
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', null, io);
|
|
1678
|
+
if (tool === 'claude' && sessionId) {
|
|
1679
|
+
updateSpecSessionIdLocal(specSessionId, tool, sessionId, io);
|
|
1680
|
+
}
|
|
1681
|
+
if (tool === 'opencode') {
|
|
1682
|
+
spawnTimestamp = Date.now();
|
|
1683
|
+
opencodeMetadataRefresher = createSpecOpencodeMetadataRefresher({
|
|
1684
|
+
specSessionId,
|
|
1685
|
+
io,
|
|
1686
|
+
spawnTimestamp,
|
|
1687
|
+
initialSessionId: resumeSessionId,
|
|
1688
|
+
});
|
|
1689
|
+
specState.metadataRefresher = opencodeMetadataRefresher;
|
|
1690
|
+
if (resumeSessionId) {
|
|
1691
|
+
const result = opencodeMetadataRefresher.refresh();
|
|
1692
|
+
if (!result.hasResolvedTitle) {
|
|
1693
|
+
opencodeMetadataRefresher.scheduleRetry();
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
const child = spawn(cmd, args, {
|
|
1698
|
+
cwd,
|
|
1699
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1700
|
+
env: { ...process.env, PWD: cwd },
|
|
1701
|
+
detached: process.platform !== 'win32',
|
|
1702
|
+
});
|
|
1703
|
+
specState.child = child;
|
|
1704
|
+
activeSpecProcesses.set(specSessionId, specState);
|
|
1705
|
+
if (tool === 'claude') {
|
|
1706
|
+
const logLines = [];
|
|
1707
|
+
const responseLines = [];
|
|
1708
|
+
let remainder = '';
|
|
1709
|
+
const stderrChunks = [];
|
|
1710
|
+
const progressUpdater = createThrottledProgressUpdater(() => {
|
|
1711
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', logLines.join('\n\n'), io, responseLines.join('\n\n'));
|
|
1712
|
+
});
|
|
1713
|
+
child.stdout?.on('data', (chunk) => {
|
|
1714
|
+
const text = remainder + chunk.toString();
|
|
1715
|
+
const lines = text.split('\n');
|
|
1716
|
+
remainder = lines.pop() ?? '';
|
|
1717
|
+
for (const line of lines) {
|
|
1718
|
+
const { logFragment, responseFragment } = parseClaudeStreamJsonLine(line);
|
|
1719
|
+
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
1720
|
+
if (logFragment !== null) {
|
|
1721
|
+
logLines.push(logFragment);
|
|
1722
|
+
}
|
|
1723
|
+
if (responseFragment !== null) {
|
|
1724
|
+
responseLines.push(responseFragment);
|
|
1725
|
+
}
|
|
1726
|
+
if (logFragment !== null || responseFragment !== null) {
|
|
1727
|
+
progressUpdater.schedule();
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
1732
|
+
child.on('close', code => {
|
|
1733
|
+
progressUpdater.cancel();
|
|
1734
|
+
if (remainder.trim()) {
|
|
1735
|
+
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
1736
|
+
const { logFragment, responseFragment } = parseClaudeStreamJsonLine(remainder);
|
|
1737
|
+
if (logFragment !== null) {
|
|
1738
|
+
logLines.push(logFragment);
|
|
1739
|
+
}
|
|
1740
|
+
if (responseFragment !== null) {
|
|
1741
|
+
responseLines.push(responseFragment);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
const errOutput = Buffer.concat(stderrChunks).toString();
|
|
1745
|
+
const finalLog = logLines.join('\n\n') || errOutput;
|
|
1746
|
+
const finalResponse = responseLines.join('\n\n') || errOutput;
|
|
1747
|
+
if (specState.stopRequested) {
|
|
1748
|
+
const preservedOutput = getPreservedSpecOutput({
|
|
1749
|
+
specSessionId,
|
|
1750
|
+
log: finalLog,
|
|
1751
|
+
response: finalResponse,
|
|
1752
|
+
});
|
|
1753
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', preservedOutput.log, io, preservedOutput.response);
|
|
1754
|
+
resolveOnce({
|
|
1755
|
+
completionStatus: 'stopped',
|
|
1756
|
+
finalResponse: preservedOutput.response,
|
|
1757
|
+
usageSnapshot,
|
|
1758
|
+
});
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
const modelError = isModelResolutionError(finalLog);
|
|
1762
|
+
if (code === 0 && !modelError) {
|
|
1763
|
+
const finalOutput = getPreservedSpecOutput({
|
|
1764
|
+
specSessionId,
|
|
1765
|
+
log: finalLog,
|
|
1766
|
+
response: finalResponse,
|
|
1767
|
+
});
|
|
1768
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', finalOutput.log, io, finalOutput.response);
|
|
1769
|
+
resolveOnce({
|
|
1770
|
+
completionStatus: 'done',
|
|
1771
|
+
finalResponse: finalOutput.response,
|
|
1772
|
+
usageSnapshot,
|
|
1773
|
+
});
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
updateSpecSessionStatusLocal(specSessionId, 'error', finalLog + (errOutput ? '\n\n' + errOutput : ''), io, finalResponse);
|
|
1777
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${finalLog}`));
|
|
1778
|
+
});
|
|
1779
|
+
child.on('error', err => {
|
|
1780
|
+
progressUpdater.cancel();
|
|
1781
|
+
if (specState.stopRequested) {
|
|
1782
|
+
const log = logLines.join('\n\n') || null;
|
|
1783
|
+
const response = responseLines.join('\n\n') || null;
|
|
1784
|
+
const preservedOutput = getPreservedSpecOutput({
|
|
1785
|
+
specSessionId,
|
|
1786
|
+
log,
|
|
1787
|
+
response,
|
|
1788
|
+
});
|
|
1789
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', preservedOutput.log, io, preservedOutput.response);
|
|
1790
|
+
resolveOnce({
|
|
1791
|
+
completionStatus: 'stopped',
|
|
1792
|
+
finalResponse: preservedOutput.response,
|
|
1793
|
+
usageSnapshot,
|
|
1794
|
+
});
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
updateSpecSessionStatusLocal(specSessionId, 'error', getErrorMessage(err), io);
|
|
1798
|
+
rejectOnce(err instanceof Error ? err : new Error(getErrorMessage(err)));
|
|
1799
|
+
});
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
const logLines = [];
|
|
1803
|
+
const responseLines = [];
|
|
1804
|
+
let remainder = '';
|
|
1805
|
+
const stderrChunks = [];
|
|
1806
|
+
const progressUpdater = createThrottledProgressUpdater(() => {
|
|
1807
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', logLines.join('\n\n'), io, responseLines.join('\n\n'));
|
|
1808
|
+
});
|
|
1809
|
+
child.stdout?.on('data', (chunk) => {
|
|
1810
|
+
const text = remainder + chunk.toString();
|
|
1811
|
+
const lines = text.split('\n');
|
|
1812
|
+
remainder = lines.pop() ?? '';
|
|
1813
|
+
for (const line of lines) {
|
|
1814
|
+
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, line);
|
|
1815
|
+
const { logFragment, responseFragment } = parseOpencodeJsonLine(line, cwd);
|
|
1816
|
+
if (logFragment !== null) {
|
|
1817
|
+
logLines.push(logFragment);
|
|
1818
|
+
}
|
|
1819
|
+
if (responseFragment !== null) {
|
|
1820
|
+
responseLines.push(responseFragment);
|
|
1821
|
+
}
|
|
1822
|
+
if (logFragment !== null || responseFragment !== null) {
|
|
1823
|
+
progressUpdater.schedule();
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
1828
|
+
if (!resumeSessionId) {
|
|
1829
|
+
opencodeMetadataRefresher?.scheduleRetry();
|
|
1830
|
+
}
|
|
1831
|
+
child.on('close', code => {
|
|
1832
|
+
progressUpdater.cancel();
|
|
1833
|
+
if (remainder.trim()) {
|
|
1834
|
+
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
1835
|
+
const { logFragment, responseFragment } = parseOpencodeJsonLine(remainder, cwd);
|
|
1836
|
+
if (logFragment !== null) {
|
|
1837
|
+
logLines.push(logFragment);
|
|
1838
|
+
}
|
|
1839
|
+
if (responseFragment !== null) {
|
|
1840
|
+
responseLines.push(responseFragment);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
const stderrOutput = Buffer.concat(stderrChunks).toString();
|
|
1844
|
+
const log = logLines.join('\n\n') || stderrOutput;
|
|
1845
|
+
const finalResponse = responseLines.join('\n\n') || stderrOutput;
|
|
1846
|
+
const preservedOutput = specState.stopRequested
|
|
1847
|
+
? getPreservedSpecOutput({
|
|
1848
|
+
specSessionId,
|
|
1849
|
+
log,
|
|
1850
|
+
response: finalResponse,
|
|
1851
|
+
})
|
|
1852
|
+
: null;
|
|
1853
|
+
const metadataResult = opencodeMetadataRefresher?.refresh();
|
|
1854
|
+
const resolvedOpencodeSessionId = metadataResult?.sessionId
|
|
1855
|
+
?? preservedOutput?.opencodeSessionId
|
|
1856
|
+
?? getSpecSession(specSessionId)?.opencode_session_id
|
|
1857
|
+
?? resumeSessionId;
|
|
1858
|
+
if (specState.stopRequested) {
|
|
1859
|
+
if (resolvedOpencodeSessionId) {
|
|
1860
|
+
usageSnapshot = resolveOpencodeUsageSnapshot({
|
|
1861
|
+
current: usageSnapshot,
|
|
1862
|
+
sessionId: resolvedOpencodeSessionId,
|
|
1863
|
+
spawnTimestamp,
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', preservedOutput?.log ?? null, io, preservedOutput?.response ?? null);
|
|
1867
|
+
resolveOnce({
|
|
1868
|
+
completionStatus: 'stopped',
|
|
1869
|
+
finalResponse: preservedOutput?.response ?? null,
|
|
1870
|
+
usageSnapshot,
|
|
1871
|
+
});
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
const modelError = isModelResolutionError(log);
|
|
1875
|
+
if (code === 0 && !modelError) {
|
|
1876
|
+
const finalOutput = getPreservedSpecOutput({
|
|
1877
|
+
specSessionId,
|
|
1878
|
+
log,
|
|
1879
|
+
response: finalResponse,
|
|
1880
|
+
});
|
|
1881
|
+
if (resolvedOpencodeSessionId && spawnTimestamp !== null) {
|
|
1882
|
+
usageSnapshot = resolveOpencodeUsageSnapshot({
|
|
1883
|
+
current: usageSnapshot,
|
|
1884
|
+
sessionId: resolvedOpencodeSessionId,
|
|
1885
|
+
spawnTimestamp,
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', finalOutput.log, io, finalOutput.response);
|
|
1889
|
+
resolveOnce({
|
|
1890
|
+
completionStatus: 'done',
|
|
1891
|
+
finalResponse: finalOutput.response,
|
|
1892
|
+
usageSnapshot,
|
|
1893
|
+
});
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
updateSpecSessionStatusLocal(specSessionId, 'error', log, io, finalResponse);
|
|
1897
|
+
rejectOnce(new Error(`Agent exited with code ${code ?? 'null'}\n${log}`));
|
|
1898
|
+
});
|
|
1899
|
+
child.on('error', err => {
|
|
1900
|
+
progressUpdater.cancel();
|
|
1901
|
+
if (specState.stopRequested) {
|
|
1902
|
+
const log = logLines.join('\n\n') || null;
|
|
1903
|
+
const response = responseLines.join('\n\n') || null;
|
|
1904
|
+
const preservedOutput = getPreservedSpecOutput({
|
|
1905
|
+
specSessionId,
|
|
1906
|
+
log,
|
|
1907
|
+
response,
|
|
1908
|
+
});
|
|
1909
|
+
updateSpecSessionStatusLocal(specSessionId, 'running', preservedOutput.log, io, preservedOutput.response);
|
|
1910
|
+
resolveOnce({
|
|
1911
|
+
completionStatus: 'stopped',
|
|
1912
|
+
finalResponse: preservedOutput.response,
|
|
1913
|
+
usageSnapshot,
|
|
1914
|
+
});
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
updateSpecSessionStatusLocal(specSessionId, 'error', getErrorMessage(err), io);
|
|
1918
|
+
rejectOnce(err instanceof Error ? err : new Error(getErrorMessage(err)));
|
|
1919
|
+
});
|
|
1920
|
+
});
|
|
1921
|
+
};
|
|
1448
1922
|
//# sourceMappingURL=agent.js.map
|