@straiffi/archon 1.8.22 → 1.10.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/{ActivityView-CdEPjmqD.js → ActivityView-Y5IaJiim.js} +1 -1
- package/dist/client/assets/GitHubActionsDialog-GWVxWGPK.js +1 -0
- package/dist/client/assets/VerificationDialog-C4RMuI4y.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BrZ08mqq.js → architectureDiagram-3BPJPVTR-CeM6gbLN.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-b93XwJ6Z.js → blockDiagram-GPEHLZMM-BP4hta96.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BKOdbZ_v.js → c4Diagram-AAUBKEIU-5aJScvhn.js} +1 -1
- package/dist/client/assets/channel-B3DjZXbm.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-D7Vxn_1m.js → chunk-2J33WTMH-BKc2IelV.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-B1zo2UZq.js → chunk-3OPIFGDE-DCc15la5.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-BajvkDdQ.js → chunk-5ZQYHXKU-DSD9HbTp.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-CSH7cVpV.js → chunk-727SXJPM-DrAjnKiA.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DE-wgUNP.js → chunk-AQP2D5EJ-DxKnpHfQ.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-Ba7JgCEf.js → chunk-CSCIHK7Q-DLejrAB_.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-0o3YWkHM.js → chunk-KSCS5N6A-BWRNmF8S.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-DihWczP8.js → chunk-L5ZTLDWV-D48i966f.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-Bxnu-hme.js → chunk-LZXEDZCA-DLhg6Bok.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-CoaR5kmP.js → chunk-ND2GUHAM-B4-7cOEq.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-By92Rd3d.js → chunk-NZK2D7GU-D2ttXA_d.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-D83Pkgfu.js → chunk-O5CBEL6O-BrWrOVCq.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-v8OVf5Lz.js → chunk-WU5MYG2G-CLcPwxPe.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DproSkn2.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-JVMMzJxJ.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-Dq1ALF7Z.js → dagre-BM42HDAG-DIXN8_VX.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-1t1_TKtu.js → diagram-2AECGRRQ-D2wjdCij.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DtvYRL6f.js → diagram-5GNKFQAL-E1Og_PHE.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-PRNrSX5F.js → diagram-KO2AKTUF-B6P1Ud8a.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DaTX0DhT.js → diagram-LMA3HP47-C3Jxq56v.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C2p9p-92.js → diagram-OG6HWLK6-Do9LVhdL.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DKzLtnp-.js → erDiagram-TEJ5UH35-DLb5E4-f.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-GdL8lm6e.js → flowDiagram-I6XJVG4X-CcTs_wU9.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-Ok-4XYwT.js → ganttDiagram-6RSMTGT7-q9ez5fri.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DSMrOCK7.js → gitGraphDiagram-PVQCEYII-BcaKiz4c.js} +1 -1
- package/dist/client/assets/index-1CokwSFm.css +2 -0
- package/dist/client/assets/index-CSOKbu3w.js +276 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Dl4hRXis.js → infoDiagram-5YYISTIA-Cu0w4frE.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C1Dc1fTT.js → ishikawaDiagram-YF4QCWOH-CJ47Z-ak.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-CUscmJze.js → journeyDiagram-JHISSGLW-fAL_Zcvs.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-C0nwYjQV.js → kanban-definition-UN3LZRKU-BTNIybMC.js} +1 -1
- package/dist/client/assets/{line-ByiuEIg_.js → line-BvsadeZR.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-B-XdAyDR.js → mermaid-parser.core-C0EkH2y_.js} +1 -1
- package/dist/client/assets/{mermaid.core-D_bIfuZD.js → mermaid.core-BU5QIIbY.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BGNiPJDK.js → mindmap-definition-RKZ34NQL-CWbSYK8f.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CP6FpAoO.js → pieDiagram-4H26LBE5-t4w2cB-y.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-dsAWBAQn.js → quadrantDiagram-W4KKPZXB-Cqasvrpa.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-DJH-zHqF.js → requirementDiagram-4Y6WPE33-_l-mY-tb.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BJMnZrqH.js → sankeyDiagram-5OEKKPKP-Cr9YrZ-Y.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CzNjUSlM.js → sequenceDiagram-3UESZ5HK-DvzZhQpb.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-jppMhOwh.js → stateDiagram-AJRCARHV-cFjPpAO3.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-btHMH5Cg.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BX2CI5H_.js → timeline-definition-PNZ67QCA-BjRNrEJY.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-Bd1KZaXh.js → vennDiagram-CIIHVFJN-xs9afjrw.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-D-0BNz49.js → wardleyDiagram-YWT4CUSO-DH68ZYti.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-DbYa0ZWK.js → xychartDiagram-2RQKCTM6-DLW5ktTU.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +231 -14
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +93 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +368 -143
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/agentProvider.js.map +1 -1
- package/dist/server/lib/approvalReconciliation.js +14 -2
- package/dist/server/lib/approvalReconciliation.js.map +1 -1
- package/dist/server/lib/buildExecutionEnvironment.js +251 -57
- package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/chatOperations.js +125 -3
- package/dist/server/lib/chatOperations.js.map +1 -1
- package/dist/server/lib/chatPostBuildRuns.js +12 -6
- package/dist/server/lib/chatPostBuildRuns.js.map +1 -1
- package/dist/server/lib/chatTargets.js +2 -1
- package/dist/server/lib/chatTargets.js.map +1 -1
- package/dist/server/lib/checkDefinitionCwd.js +34 -0
- package/dist/server/lib/checkDefinitionCwd.js.map +1 -0
- package/dist/server/lib/checkExecutionEnvironment.js +95 -0
- package/dist/server/lib/checkExecutionEnvironment.js.map +1 -0
- package/dist/server/lib/checkPolicyDefinitionIds.js +41 -0
- package/dist/server/lib/checkPolicyDefinitionIds.js.map +1 -0
- package/dist/server/lib/checkRepairDispatch.js +2 -0
- package/dist/server/lib/checkRepairDispatch.js.map +1 -1
- package/dist/server/lib/checkRunner.js +371 -175
- package/dist/server/lib/checkRunner.js.map +1 -1
- package/dist/server/lib/checkRuns.js +99 -8
- package/dist/server/lib/checkRuns.js.map +1 -1
- package/dist/server/lib/claudeAgentProvider.js +3 -6
- package/dist/server/lib/claudeAgentProvider.js.map +1 -1
- package/dist/server/lib/claudeStdin.js +27 -0
- package/dist/server/lib/claudeStdin.js.map +1 -0
- package/dist/server/lib/completedAssistantOutput.js +38 -0
- package/dist/server/lib/completedAssistantOutput.js.map +1 -0
- package/dist/server/lib/consequentialActionAuthority.js +99 -8
- package/dist/server/lib/consequentialActionAuthority.js.map +1 -1
- package/dist/server/lib/consequentialActions.js +42 -2
- package/dist/server/lib/consequentialActions.js.map +1 -1
- package/dist/server/lib/dockerSandboxCredentials.js +10 -3
- package/dist/server/lib/dockerSandboxCredentials.js.map +1 -1
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js +9 -8
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js +167 -75
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionProvider.js +22 -14
- package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -1
- package/dist/server/lib/dockerSandboxNetworkPolicy.js +23 -14
- package/dist/server/lib/dockerSandboxNetworkPolicy.js.map +1 -1
- package/dist/server/lib/dockerSandboxReleaseProfiles.js +91 -0
- package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -0
- package/dist/server/lib/dockerSandboxRuntimeController.js +16 -1
- package/dist/server/lib/dockerSandboxRuntimeController.js.map +1 -1
- package/dist/server/lib/executionCredentialCapabilities.js +9 -6
- package/dist/server/lib/executionCredentialCapabilities.js.map +1 -1
- package/dist/server/lib/executionEnvironment.js.map +1 -1
- package/dist/server/lib/executionEnvironmentLifecycle.js +7 -0
- package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -1
- package/dist/server/lib/executionEnvironmentOperations.js +241 -0
- package/dist/server/lib/executionEnvironmentOperations.js.map +1 -0
- package/dist/server/lib/executionEnvironmentReconciliation.js +98 -3
- package/dist/server/lib/executionEnvironmentReconciliation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentRoutes.js +83 -29
- package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidation.js +19 -5
- package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidationOperations.js +58 -31
- package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
- package/dist/server/lib/executionEnvironments.js +187 -9
- package/dist/server/lib/executionEnvironments.js.map +1 -1
- package/dist/server/lib/executionProfileCheckPolicy.js +14 -0
- package/dist/server/lib/executionProfileCheckPolicy.js.map +1 -0
- package/dist/server/lib/executionProfileRoutes.js +9 -1
- package/dist/server/lib/executionProfileRoutes.js.map +1 -1
- package/dist/server/lib/executionProfileStore.js +5 -3
- package/dist/server/lib/executionProfileStore.js.map +1 -1
- package/dist/server/lib/executionProfiles.js +97 -15
- package/dist/server/lib/executionProfiles.js.map +1 -1
- package/dist/server/lib/executionReconciliation.js +43 -6
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRunAuthority.js +115 -24
- package/dist/server/lib/executionRunAuthority.js.map +1 -1
- package/dist/server/lib/executionTargets.js +30 -4
- package/dist/server/lib/executionTargets.js.map +1 -1
- package/dist/server/lib/executionWorkloads.js +75 -0
- package/dist/server/lib/executionWorkloads.js.map +1 -0
- package/dist/server/lib/git.js +10 -1
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/opencodeAgentProvider.js +3 -1
- package/dist/server/lib/opencodeAgentProvider.js.map +1 -1
- package/dist/server/lib/opencodeSessionOperations.js +5 -3
- package/dist/server/lib/opencodeSessionOperations.js.map +1 -1
- package/dist/server/lib/planQuestions.js +2 -1
- package/dist/server/lib/planQuestions.js.map +1 -1
- package/dist/server/lib/postBuildCheckRuns.js +9 -5
- package/dist/server/lib/postBuildCheckRuns.js.map +1 -1
- package/dist/server/lib/preview.js +47 -6
- package/dist/server/lib/preview.js.map +1 -1
- package/dist/server/lib/previewProxy.js +11 -3
- package/dist/server/lib/previewProxy.js.map +1 -1
- package/dist/server/lib/productionCheckApprovals.js +97 -35
- package/dist/server/lib/productionCheckApprovals.js.map +1 -1
- package/dist/server/lib/projectMemorySuggestions.js +29 -6
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
- package/dist/server/lib/requiredCheckPolicy.js +3 -40
- package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
- package/dist/server/lib/reviewAgentWatchdog.js +75 -0
- package/dist/server/lib/reviewAgentWatchdog.js.map +1 -0
- package/dist/server/lib/reviewFindingReconciliation.js +176 -0
- package/dist/server/lib/reviewFindingReconciliation.js.map +1 -0
- package/dist/server/lib/reviewFindings.js +45 -17
- package/dist/server/lib/reviewFindings.js.map +1 -1
- package/dist/server/lib/run.js +370 -80
- package/dist/server/lib/run.js.map +1 -1
- package/dist/server/lib/serviceRuns.js +196 -0
- package/dist/server/lib/serviceRuns.js.map +1 -0
- package/dist/server/lib/sessionTargets.js +34 -0
- package/dist/server/lib/sessionTargets.js.map +1 -1
- package/dist/server/lib/shell.js +20 -0
- package/dist/server/lib/shell.js.map +1 -1
- package/dist/server/lib/specArtifact.js +36 -15
- package/dist/server/lib/specArtifact.js.map +1 -1
- package/dist/server/lib/specBundleGroups.js +4 -4
- package/dist/server/lib/specBundleGroups.js.map +1 -1
- package/dist/server/lib/specOperations.js +2 -0
- package/dist/server/lib/specOperations.js.map +1 -1
- package/dist/server/lib/specReviewOperations.js +2 -0
- package/dist/server/lib/specReviewOperations.js.map +1 -1
- package/dist/server/lib/specTicketMaterialization.js +37 -24
- package/dist/server/lib/specTicketMaterialization.js.map +1 -1
- package/dist/server/lib/ticketFollowUpOperations.js +9 -4
- package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
- package/dist/server/lib/ticketRunOperations.js +1 -0
- package/dist/server/lib/ticketRunOperations.js.map +1 -1
- package/dist/server/lib/types.js +1 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/lib/workloadExecutionEnvironment.js +2 -0
- package/dist/server/lib/workloadExecutionEnvironment.js.map +1 -0
- package/dist/server/lib/workspaceIdentity.js +30 -6
- package/dist/server/lib/workspaceIdentity.js.map +1 -1
- package/dist/server/workers/build.js +39 -8
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +142 -15
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +77 -20
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +295 -48
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/planFollowUp.js +77 -7
- package/dist/server/workers/planFollowUp.js.map +1 -1
- package/dist/server/workers/review.js +153 -40
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specReview.js +43 -5
- package/dist/server/workers/specReview.js.map +1 -1
- package/dist/server/workers/specSession.js +156 -31
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/GitHubActionsDialog-CA189pr2.js +0 -1
- package/dist/client/assets/VerificationDialog-BPVkhKRj.js +0 -4
- package/dist/client/assets/channel-D0zqzBT_.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ChhyvHH2.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Cl02kurK.js +0 -1
- package/dist/client/assets/index-Bs8KsdV9.js +0 -276
- package/dist/client/assets/index-DdwM8Wgt.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CLR1WgdV.js +0 -1
package/dist/server/lib/agent.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
|
-
import {
|
|
4
|
-
import { extname, isAbsolute, join, relative, resolve, sep } from 'path';
|
|
3
|
+
import { isAbsolute, relative, resolve, sep } from 'path';
|
|
5
4
|
import db from '../db.js';
|
|
6
5
|
import { getChatSession, updateChatSessionUsageSnapshot } from './chats.js';
|
|
7
6
|
import { normalizeClaudeModelId, normalizeModelId } from './models.js';
|
|
@@ -23,6 +22,8 @@ import { createHostLauncherEnvironment, createLegacyHostRuntimeEnvironment, crea
|
|
|
23
22
|
import { OpencodeModelResolutionError, parseOpencodeStderrModelResolutionError, } from './opencodeErrors.js';
|
|
24
23
|
import { extractLeadingOpencodeJsonObjects } from './opencodePromptOutput.js';
|
|
25
24
|
import { assertAgentSpawnPermit, AgentSpawnPermitRevokedError } from './agentSpawnPermits.js';
|
|
25
|
+
import { buildClaudeStdinMessage } from './claudeStdin.js';
|
|
26
|
+
import { createReviewAgentWatchdog, resolveWithin, REVIEW_AGENT_CLOSE_ENRICHMENT_TIMEOUT_MS, REVIEW_AGENT_INACTIVITY_TIMEOUT_MS, REVIEW_AGENT_KILL_GRACE_MS, } from './reviewAgentWatchdog.js';
|
|
26
27
|
const withTranscriptRuns = (value, transcriptRuns) => {
|
|
27
28
|
return transcriptRuns ? { ...value, transcriptRuns } : value;
|
|
28
29
|
};
|
|
@@ -57,33 +58,6 @@ const resolveAgentModel = (tool, model) => {
|
|
|
57
58
|
}
|
|
58
59
|
return normalizeModelId(model) || null;
|
|
59
60
|
};
|
|
60
|
-
const CLAUDE_IMAGE_EXTENSIONS = {
|
|
61
|
-
'.png': 'image/png',
|
|
62
|
-
'.jpg': 'image/jpeg',
|
|
63
|
-
'.jpeg': 'image/jpeg',
|
|
64
|
-
'.webp': 'image/webp',
|
|
65
|
-
'.gif': 'image/gif',
|
|
66
|
-
};
|
|
67
|
-
const buildClaudeStdinMessage = (prompt, files, cwd) => {
|
|
68
|
-
if (files.length === 0) {
|
|
69
|
-
return JSON.stringify({ type: 'user', message: { role: 'user', content: prompt } });
|
|
70
|
-
}
|
|
71
|
-
const content = [{ type: 'text', text: prompt }];
|
|
72
|
-
for (const filePath of files) {
|
|
73
|
-
const absolutePath = isAbsolute(filePath) ? filePath : join(cwd, filePath);
|
|
74
|
-
const ext = extname(absolutePath).toLowerCase();
|
|
75
|
-
const mediaType = CLAUDE_IMAGE_EXTENSIONS[ext];
|
|
76
|
-
if (!mediaType) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const data = readFileSync(absolutePath).toString('base64');
|
|
80
|
-
content.push({
|
|
81
|
-
type: 'image',
|
|
82
|
-
source: { type: 'base64', media_type: mediaType, data },
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return JSON.stringify({ type: 'user', message: { role: 'user', content } });
|
|
86
|
-
};
|
|
87
61
|
const writeClaudeStdin = (child, message) => {
|
|
88
62
|
const stdin = child.stdin;
|
|
89
63
|
if (!stdin) {
|
|
@@ -454,9 +428,9 @@ export const readOpencodeUsageSnapshot = async ({ sessionId, spawnTimestamp = nu
|
|
|
454
428
|
}
|
|
455
429
|
return snapshot;
|
|
456
430
|
};
|
|
457
|
-
const resolveOpencodeUsageSnapshot = async ({ current, sessionId, spawnTimestamp = null, }) => {
|
|
431
|
+
const resolveOpencodeUsageSnapshot = async ({ current, sessionId, spawnTimestamp = null, sessionOperations = hostOpencodeSessionOperations, }) => {
|
|
458
432
|
try {
|
|
459
|
-
return mergeChatUsageSnapshot(current, await readOpencodeUsageSnapshot({ sessionId, spawnTimestamp }));
|
|
433
|
+
return mergeChatUsageSnapshot(current, await readOpencodeUsageSnapshot({ sessionId, spawnTimestamp, sessionOperations }));
|
|
460
434
|
}
|
|
461
435
|
catch {
|
|
462
436
|
return current;
|
|
@@ -532,7 +506,7 @@ const updateChatSessionOpencodeMetadata = (chatSessionId, sessionId, title, io)
|
|
|
532
506
|
io.emit('chat-session:updated', chatSession);
|
|
533
507
|
}
|
|
534
508
|
};
|
|
535
|
-
const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, initialSessionId = null, }) => {
|
|
509
|
+
const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, initialSessionId = null, sessionOperations = hostOpencodeSessionOperations, }) => {
|
|
536
510
|
let lookupTimer = null;
|
|
537
511
|
let stopped = false;
|
|
538
512
|
let knownSessionId = initialSessionId;
|
|
@@ -547,12 +521,12 @@ const createOpencodeMetadataRefresher = ({ chatSessionId, io, spawnTimestamp, in
|
|
|
547
521
|
};
|
|
548
522
|
const readCurrentSession = async () => {
|
|
549
523
|
if (knownSessionId) {
|
|
550
|
-
return
|
|
524
|
+
return sessionOperations.findSessionById(knownSessionId);
|
|
551
525
|
}
|
|
552
526
|
if (spawnTimestamp === null) {
|
|
553
527
|
return null;
|
|
554
528
|
}
|
|
555
|
-
return
|
|
529
|
+
return sessionOperations.findLatestSessionAfter(spawnTimestamp);
|
|
556
530
|
};
|
|
557
531
|
const refresh = async () => {
|
|
558
532
|
if (stopped) {
|
|
@@ -1316,12 +1290,14 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1316
1290
|
};
|
|
1317
1291
|
const applySnapshot = ({ callId, label, status, log, response, sessionId, }) => {
|
|
1318
1292
|
let run = findRun(callId);
|
|
1293
|
+
let created = false;
|
|
1319
1294
|
if (!run) {
|
|
1320
1295
|
handle([{ kind: 'start', callId, label, sessionId }]);
|
|
1321
1296
|
run = findRun(callId);
|
|
1297
|
+
created = Boolean(run);
|
|
1322
1298
|
}
|
|
1323
1299
|
if (!run) {
|
|
1324
|
-
return;
|
|
1300
|
+
return false;
|
|
1325
1301
|
}
|
|
1326
1302
|
bindSessionId(run, sessionId);
|
|
1327
1303
|
updateLabel(run, label);
|
|
@@ -1330,7 +1306,7 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1330
1306
|
const nextStatus = run.status !== 'running' && status === 'running' ? run.status : status;
|
|
1331
1307
|
const hasChanged = run.log !== nextLog || run.response !== nextResponse || run.status !== nextStatus;
|
|
1332
1308
|
if (!hasChanged) {
|
|
1333
|
-
return;
|
|
1309
|
+
return created;
|
|
1334
1310
|
}
|
|
1335
1311
|
run.log = nextLog;
|
|
1336
1312
|
run.response = nextResponse;
|
|
@@ -1343,7 +1319,9 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1343
1319
|
finalizeAgentRun(run.id, nextStatus, run.response || null);
|
|
1344
1320
|
}
|
|
1345
1321
|
emitUpdate();
|
|
1322
|
+
return true;
|
|
1346
1323
|
};
|
|
1324
|
+
const hasRunningSubagents = () => trackedRuns.some(run => run.status === 'running');
|
|
1347
1325
|
const finalize = (status, parentLog, parentResponse) => {
|
|
1348
1326
|
if (trackedRuns.length === 0 && !mainRun) {
|
|
1349
1327
|
return null;
|
|
@@ -1454,11 +1432,13 @@ export const createSubagentRunTracker = ({ parentType, parentId, io, mainRun, ex
|
|
|
1454
1432
|
updateAgentRunSessionId(mainRun.id, normalized);
|
|
1455
1433
|
}
|
|
1456
1434
|
};
|
|
1457
|
-
return { applySnapshot, handle, finalize, setMainSessionId, updateMain };
|
|
1435
|
+
return { applySnapshot, handle, finalize, hasRunningSubagents, setMainSessionId, updateMain };
|
|
1458
1436
|
};
|
|
1459
1437
|
export const reconstructOpencodeSubagentTranscript = (parts, cwd) => {
|
|
1460
1438
|
let log = '';
|
|
1461
1439
|
let response = '';
|
|
1440
|
+
let finalResponse = '';
|
|
1441
|
+
let finalResponseMessageId = null;
|
|
1462
1442
|
for (const persistedPart of parts) {
|
|
1463
1443
|
if (persistedPart.role && persistedPart.role !== 'assistant') {
|
|
1464
1444
|
continue;
|
|
@@ -1479,11 +1459,22 @@ export const reconstructOpencodeSubagentTranscript = (parts, cwd) => {
|
|
|
1479
1459
|
if (parsed.responseFragment) {
|
|
1480
1460
|
response = appendTranscriptText(response, parsed.responseFragment);
|
|
1481
1461
|
}
|
|
1462
|
+
const finalAnswerFragments = (parsed.recoveryEvents ?? []).flatMap(event => (event.type === 'final_answer' ? [event.text] : []));
|
|
1463
|
+
if (finalAnswerFragments.length > 0) {
|
|
1464
|
+
const messageId = persistedPart.messageId ?? persistedPart.id;
|
|
1465
|
+
if (finalResponseMessageId !== messageId) {
|
|
1466
|
+
finalResponse = '';
|
|
1467
|
+
finalResponseMessageId = messageId;
|
|
1468
|
+
}
|
|
1469
|
+
for (const fragment of finalAnswerFragments) {
|
|
1470
|
+
finalResponse = appendTranscriptText(finalResponse, fragment);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1482
1473
|
}
|
|
1483
|
-
return { log: log || null, response: response || null };
|
|
1474
|
+
return { finalResponse: finalResponse || null, log: log || null, response: response || null };
|
|
1484
1475
|
};
|
|
1485
|
-
export const readOpencodeSessionTranscript = async ({ sessionId, since, cwd, }) => {
|
|
1486
|
-
const reader = createOpencodeTaskSessionReader();
|
|
1476
|
+
export const readOpencodeSessionTranscript = async ({ sessionId, since, cwd, sessionOperations = hostOpencodeSessionOperations, }) => {
|
|
1477
|
+
const reader = createOpencodeTaskSessionReader(sessionOperations);
|
|
1487
1478
|
if (!reader) {
|
|
1488
1479
|
return null;
|
|
1489
1480
|
}
|
|
@@ -1497,7 +1488,7 @@ export const readOpencodeSessionTranscript = async ({ sessionId, since, cwd, })
|
|
|
1497
1488
|
reader.close();
|
|
1498
1489
|
}
|
|
1499
1490
|
};
|
|
1500
|
-
export const createOpencodeSubagentMonitor = ({ initialParentSessionId, spawnTimestamp, cwd, tracker, onParentSessionId, taskSessionReader, discoverParentSession = true, parentDiscoveryDelayMs = 0, }) => {
|
|
1491
|
+
export const createOpencodeSubagentMonitor = ({ initialParentSessionId, spawnTimestamp, cwd, tracker, onParentSessionId, onActivity, taskSessionReader, discoverParentSession = true, parentDiscoveryDelayMs = 0, }) => {
|
|
1501
1492
|
const reader = taskSessionReader === undefined ? createOpencodeTaskSessionReader() : taskSessionReader;
|
|
1502
1493
|
let parentSessionId = initialParentSessionId;
|
|
1503
1494
|
let timer = null;
|
|
@@ -1557,7 +1548,7 @@ export const createOpencodeSubagentMonitor = ({ initialParentSessionId, spawnTim
|
|
|
1557
1548
|
const transcript = childSessionId
|
|
1558
1549
|
? reconstructOpencodeSubagentTranscript(await reader.listSessionParts(childSessionId), cwd)
|
|
1559
1550
|
: { log: null, response: null };
|
|
1560
|
-
tracker.applySnapshot({
|
|
1551
|
+
const changed = tracker.applySnapshot({
|
|
1561
1552
|
callId: invocation.callId,
|
|
1562
1553
|
label: invocation.label,
|
|
1563
1554
|
status: invocation.status,
|
|
@@ -1565,6 +1556,9 @@ export const createOpencodeSubagentMonitor = ({ initialParentSessionId, spawnTim
|
|
|
1565
1556
|
response: transcript.response ?? invocation.output,
|
|
1566
1557
|
sessionId: childSessionId ?? null,
|
|
1567
1558
|
});
|
|
1559
|
+
if (changed) {
|
|
1560
|
+
onActivity?.();
|
|
1561
|
+
}
|
|
1568
1562
|
}
|
|
1569
1563
|
}
|
|
1570
1564
|
catch {
|
|
@@ -1648,6 +1642,19 @@ const interruptProcess = (state, signal) => {
|
|
|
1648
1642
|
}
|
|
1649
1643
|
killProcess(state.child, signal);
|
|
1650
1644
|
};
|
|
1645
|
+
const interruptReviewProcess = async (state, child, signal) => {
|
|
1646
|
+
if (!isProcessAlive(child)) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const interrupt = state.interrupts.get(child);
|
|
1650
|
+
if (interrupt) {
|
|
1651
|
+
await Promise.resolve(interrupt(signal)).catch(() => {
|
|
1652
|
+
// The execution provider owns interruption failure reporting.
|
|
1653
|
+
});
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
killProcess(child, signal);
|
|
1657
|
+
};
|
|
1651
1658
|
const waitForProcessExit = async (proc, timeoutMs) => {
|
|
1652
1659
|
const deadline = Date.now() + timeoutMs;
|
|
1653
1660
|
await new Promise(resolve => {
|
|
@@ -1682,6 +1689,7 @@ export const registerChatProcessPlaceholder = (chatSessionId) => {
|
|
|
1682
1689
|
const state = {
|
|
1683
1690
|
child: null,
|
|
1684
1691
|
interrupt: null,
|
|
1692
|
+
abortController: new AbortController(),
|
|
1685
1693
|
researchChildren: new Set(),
|
|
1686
1694
|
stopRequested: false,
|
|
1687
1695
|
metadataRefresher: null,
|
|
@@ -1722,12 +1730,13 @@ const untrackSpecProcess = (specSessionId, state) => {
|
|
|
1722
1730
|
// Registers a cancellation token for a spec session before its child process
|
|
1723
1731
|
// exists. This closes the race window between the worker flipping the DB
|
|
1724
1732
|
// status to 'running' and runSpecAgent() registering the abortable child: an
|
|
1725
|
-
// interrupt during that window sets stopRequested on
|
|
1726
|
-
// the worker honors
|
|
1733
|
+
// interrupt during that window aborts preparation and sets stopRequested on
|
|
1734
|
+
// this placeholder, which the worker honors before starting the agent.
|
|
1727
1735
|
export const registerSpecProcessPlaceholder = (specSessionId) => {
|
|
1728
1736
|
const state = {
|
|
1729
1737
|
child: null,
|
|
1730
1738
|
interrupt: null,
|
|
1739
|
+
abortController: new AbortController(),
|
|
1731
1740
|
researchChildren: new Set(),
|
|
1732
1741
|
stopRequested: false,
|
|
1733
1742
|
metadataRefresher: null,
|
|
@@ -1752,6 +1761,7 @@ export const registerSpecReviewProcessPlaceholder = (specReviewRunId) => {
|
|
|
1752
1761
|
const state = {
|
|
1753
1762
|
child: null,
|
|
1754
1763
|
interrupt: null,
|
|
1764
|
+
abortController: new AbortController(),
|
|
1755
1765
|
researchChildren: new Set(),
|
|
1756
1766
|
stopRequested: false,
|
|
1757
1767
|
metadataRefresher: null,
|
|
@@ -1767,7 +1777,7 @@ export const untrackSpecReviewProcessById = (specReviewRunId) => {
|
|
|
1767
1777
|
};
|
|
1768
1778
|
export const isBuildAgentRunning = (ticketId) => activeBuildProcesses.has(ticketId);
|
|
1769
1779
|
export const registerReviewProcess = (ticketId) => {
|
|
1770
|
-
const state = { children: new Set(), stopRequested: false };
|
|
1780
|
+
const state = { children: new Set(), interrupts: new Map(), stopRequested: false };
|
|
1771
1781
|
activeReviewProcesses.set(ticketId, state);
|
|
1772
1782
|
return state;
|
|
1773
1783
|
};
|
|
@@ -1823,14 +1833,10 @@ export const stopReviewAgent = async (ticketId) => {
|
|
|
1823
1833
|
}
|
|
1824
1834
|
state.stopRequested = true;
|
|
1825
1835
|
const children = [...state.children];
|
|
1826
|
-
|
|
1827
|
-
killProcess(child, 'SIGTERM');
|
|
1828
|
-
}
|
|
1836
|
+
await Promise.all(children.map(child => interruptReviewProcess(state, child, 'SIGTERM')));
|
|
1829
1837
|
await Promise.all(children.map(child => waitForProcessExit(child, 5000)));
|
|
1830
1838
|
const aliveChildren = children.filter(isProcessAlive);
|
|
1831
|
-
|
|
1832
|
-
killProcess(child, 'SIGKILL');
|
|
1833
|
-
}
|
|
1839
|
+
await Promise.all(aliveChildren.map(child => interruptReviewProcess(state, child, 'SIGKILL')));
|
|
1834
1840
|
await Promise.all(aliveChildren.map(child => waitForProcessExit(child, 1000)));
|
|
1835
1841
|
return true;
|
|
1836
1842
|
};
|
|
@@ -1840,14 +1846,25 @@ export const stopChatAgent = async (chatSessionId) => {
|
|
|
1840
1846
|
return false;
|
|
1841
1847
|
}
|
|
1842
1848
|
state.stopRequested = true;
|
|
1849
|
+
state.abortController.abort(new Error('Chat stop requested'));
|
|
1843
1850
|
state.metadataRefresher?.stop();
|
|
1844
1851
|
if (!state.child) {
|
|
1845
1852
|
return true;
|
|
1846
1853
|
}
|
|
1847
|
-
|
|
1854
|
+
if (state.interrupt) {
|
|
1855
|
+
await state.interrupt('SIGTERM');
|
|
1856
|
+
}
|
|
1857
|
+
else {
|
|
1858
|
+
killProcess(state.child, 'SIGTERM');
|
|
1859
|
+
}
|
|
1848
1860
|
await waitForProcessExit(state.child, 5000);
|
|
1849
1861
|
if (isProcessAlive(state.child)) {
|
|
1850
|
-
|
|
1862
|
+
if (state.interrupt) {
|
|
1863
|
+
await state.interrupt('SIGKILL');
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
killProcess(state.child, 'SIGKILL');
|
|
1867
|
+
}
|
|
1851
1868
|
await waitForProcessExit(state.child, 1000);
|
|
1852
1869
|
}
|
|
1853
1870
|
return true;
|
|
@@ -1858,23 +1875,43 @@ export const stopSpecAgent = async (specSessionId) => {
|
|
|
1858
1875
|
return false;
|
|
1859
1876
|
}
|
|
1860
1877
|
state.stopRequested = true;
|
|
1878
|
+
state.abortController.abort(new Error('Spec stop requested'));
|
|
1861
1879
|
state.metadataRefresher?.stop();
|
|
1862
|
-
const
|
|
1863
|
-
if (state.child) {
|
|
1864
|
-
children.push(state.child);
|
|
1865
|
-
}
|
|
1866
|
-
if (children.length === 0) {
|
|
1880
|
+
const researchChildren = [...state.researchChildren];
|
|
1881
|
+
if (researchChildren.length === 0 && !state.child) {
|
|
1867
1882
|
return true;
|
|
1868
1883
|
}
|
|
1869
|
-
for (const child of
|
|
1884
|
+
for (const child of researchChildren) {
|
|
1870
1885
|
killProcess(child, 'SIGTERM');
|
|
1871
1886
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1887
|
+
if (state.child) {
|
|
1888
|
+
if (state.interrupt) {
|
|
1889
|
+
await state.interrupt('SIGTERM');
|
|
1890
|
+
}
|
|
1891
|
+
else {
|
|
1892
|
+
killProcess(state.child, 'SIGTERM');
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
await Promise.all([
|
|
1896
|
+
...researchChildren.map(child => waitForProcessExit(child, 5000)),
|
|
1897
|
+
...(state.child ? [waitForProcessExit(state.child, 5000)] : []),
|
|
1898
|
+
]);
|
|
1899
|
+
const aliveResearchChildren = researchChildren.filter(isProcessAlive);
|
|
1900
|
+
for (const child of aliveResearchChildren) {
|
|
1875
1901
|
killProcess(child, 'SIGKILL');
|
|
1876
1902
|
}
|
|
1877
|
-
|
|
1903
|
+
if (isProcessAlive(state.child)) {
|
|
1904
|
+
if (state.interrupt) {
|
|
1905
|
+
await state.interrupt('SIGKILL');
|
|
1906
|
+
}
|
|
1907
|
+
else {
|
|
1908
|
+
killProcess(state.child, 'SIGKILL');
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
await Promise.all([
|
|
1912
|
+
...aliveResearchChildren.map(child => waitForProcessExit(child, 1000)),
|
|
1913
|
+
...(isProcessAlive(state.child) ? [waitForProcessExit(state.child, 1000)] : []),
|
|
1914
|
+
]);
|
|
1878
1915
|
return true;
|
|
1879
1916
|
};
|
|
1880
1917
|
export const stopSpecReviewAgent = async (specReviewRunId) => {
|
|
@@ -1883,6 +1920,7 @@ export const stopSpecReviewAgent = async (specReviewRunId) => {
|
|
|
1883
1920
|
return false;
|
|
1884
1921
|
}
|
|
1885
1922
|
state.stopRequested = true;
|
|
1923
|
+
state.abortController.abort(new Error('Spec review stop requested'));
|
|
1886
1924
|
state.metadataRefresher?.stop();
|
|
1887
1925
|
if (!state.child) {
|
|
1888
1926
|
return true;
|
|
@@ -1942,24 +1980,32 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
1942
1980
|
updateSessionId(ticketId, sessionId, sessionField, io);
|
|
1943
1981
|
};
|
|
1944
1982
|
const finalizeRun = (status, log, responseText, options = {}) => {
|
|
1983
|
+
const { transcriptResponseText = responseText, ...statusOptions } = options;
|
|
1945
1984
|
if (runStream) {
|
|
1946
|
-
finalizeAgentRun(runStream.runId, status,
|
|
1985
|
+
finalizeAgentRun(runStream.runId, status, transcriptResponseText);
|
|
1947
1986
|
emitRunUpdate();
|
|
1948
|
-
return subagentTracker.finalize(status, log,
|
|
1987
|
+
return subagentTracker.finalize(status, log, transcriptResponseText);
|
|
1949
1988
|
}
|
|
1950
|
-
updateStatus(ticketId, status, log, io, responseText,
|
|
1951
|
-
return subagentTracker.finalize(status, log,
|
|
1989
|
+
updateStatus(ticketId, status, log, io, responseText, statusOptions);
|
|
1990
|
+
return subagentTracker.finalize(status, log, transcriptResponseText);
|
|
1952
1991
|
};
|
|
1953
1992
|
return new Promise((resolve, reject) => {
|
|
1954
1993
|
let sessionId = null;
|
|
1955
1994
|
let spawnTimestamp = null;
|
|
1956
1995
|
let sessionLookupTimer = null;
|
|
1957
1996
|
let hasResolvedOpencodeSessionId = Boolean(resumeSessionId);
|
|
1997
|
+
let hasTrustedOpencodeSessionId = Boolean(resumeSessionId);
|
|
1958
1998
|
let opencodeSubagentMonitor = null;
|
|
1959
1999
|
let settled = false;
|
|
1960
2000
|
let startTime = null;
|
|
1961
2001
|
let firstFinalAnswerTime = null;
|
|
2002
|
+
let reviewFinalAnswerObserved = false;
|
|
1962
2003
|
let reviewChild = null;
|
|
2004
|
+
let reviewWatchdog = null;
|
|
2005
|
+
let reviewKillTimer = null;
|
|
2006
|
+
let reviewCompletionForced = false;
|
|
2007
|
+
let reviewInactivityTimeoutForced = false;
|
|
2008
|
+
let lastOutputAt = Date.now();
|
|
1963
2009
|
let executionCapableEventObserved = false;
|
|
1964
2010
|
const buildState = trackBuildProcess
|
|
1965
2011
|
? activeBuildProcesses.get(ticketId) ?? registerBuildProcessPlaceholder(ticketId)
|
|
@@ -1970,6 +2016,11 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
1970
2016
|
}
|
|
1971
2017
|
settled = true;
|
|
1972
2018
|
opencodeSubagentMonitor?.stop();
|
|
2019
|
+
reviewWatchdog?.stop();
|
|
2020
|
+
if (reviewKillTimer) {
|
|
2021
|
+
clearTimeout(reviewKillTimer);
|
|
2022
|
+
reviewKillTimer = null;
|
|
2023
|
+
}
|
|
1973
2024
|
if (sessionLookupTimer) {
|
|
1974
2025
|
clearTimeout(sessionLookupTimer);
|
|
1975
2026
|
sessionLookupTimer = null;
|
|
@@ -1979,12 +2030,13 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
1979
2030
|
}
|
|
1980
2031
|
if (reviewChild) {
|
|
1981
2032
|
reviewProcessState?.children.delete(reviewChild);
|
|
2033
|
+
reviewProcessState?.interrupts.delete(reviewChild);
|
|
1982
2034
|
}
|
|
1983
2035
|
handler(value);
|
|
1984
2036
|
};
|
|
1985
2037
|
const resolveOnce = settle(resolve);
|
|
1986
2038
|
const rejectOnce = settle(reject);
|
|
1987
|
-
const bindResolvedOpencodeSession = (resolvedId) => {
|
|
2039
|
+
const bindResolvedOpencodeSession = (resolvedId, trusted = false) => {
|
|
1988
2040
|
const normalized = resolvedId?.trim();
|
|
1989
2041
|
if (!normalized) {
|
|
1990
2042
|
return;
|
|
@@ -1992,6 +2044,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
1992
2044
|
const shouldPersist = sessionId !== normalized;
|
|
1993
2045
|
sessionId = normalized;
|
|
1994
2046
|
hasResolvedOpencodeSessionId = true;
|
|
2047
|
+
hasTrustedOpencodeSessionId = hasTrustedOpencodeSessionId || trusted;
|
|
1995
2048
|
opencodeSubagentMonitor?.setParentSessionId(normalized);
|
|
1996
2049
|
if (shouldPersist) {
|
|
1997
2050
|
writeRunSessionId(normalized);
|
|
@@ -2078,9 +2131,7 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2078
2131
|
}
|
|
2079
2132
|
if (reviewProcessState) {
|
|
2080
2133
|
reviewProcessState.children.add(child);
|
|
2081
|
-
|
|
2082
|
-
killProcess(child, 'SIGTERM');
|
|
2083
|
-
}
|
|
2134
|
+
reviewProcessState.interrupts.set(child, interrupt);
|
|
2084
2135
|
}
|
|
2085
2136
|
void placementFailure.then(failure => {
|
|
2086
2137
|
if (!failure || settled) {
|
|
@@ -2105,9 +2156,12 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2105
2156
|
onParentSessionId: discoveredSessionId => {
|
|
2106
2157
|
bindResolvedOpencodeSession(discoveredSessionId);
|
|
2107
2158
|
},
|
|
2159
|
+
onActivity: () => {
|
|
2160
|
+
lastOutputAt = Date.now();
|
|
2161
|
+
},
|
|
2108
2162
|
});
|
|
2109
2163
|
opencodeSubagentMonitor.start();
|
|
2110
|
-
bindResolvedOpencodeSession(resumeSessionId);
|
|
2164
|
+
bindResolvedOpencodeSession(resumeSessionId, true);
|
|
2111
2165
|
}
|
|
2112
2166
|
if (tool === 'claude') {
|
|
2113
2167
|
const logLines = [];
|
|
@@ -2196,6 +2250,9 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2196
2250
|
const transcriptRuns = finalizeRun('error', getErrorMessage(resolvedError), null);
|
|
2197
2251
|
rejectOnce(attachTranscriptRunsToError(resolvedError, transcriptRuns));
|
|
2198
2252
|
});
|
|
2253
|
+
if (reviewProcessState?.stopRequested) {
|
|
2254
|
+
void interruptReviewProcess(reviewProcessState, child, 'SIGTERM');
|
|
2255
|
+
}
|
|
2199
2256
|
return;
|
|
2200
2257
|
}
|
|
2201
2258
|
const logLines = [];
|
|
@@ -2207,7 +2264,27 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2207
2264
|
const progressUpdater = createThrottledProgressUpdater(() => {
|
|
2208
2265
|
writeRunStatus('running', logLines.join('\n\n'), responseLines.join('\n\n'));
|
|
2209
2266
|
});
|
|
2267
|
+
if (reviewProcessState && startTime !== null) {
|
|
2268
|
+
reviewWatchdog = createReviewAgentWatchdog({
|
|
2269
|
+
getLastOutputAt: () => lastOutputAt,
|
|
2270
|
+
checkForCompletion: async () => reviewFinalAnswerObserved && !subagentTracker.hasRunningSubagents(),
|
|
2271
|
+
onTrigger: async (trigger) => {
|
|
2272
|
+
if (trigger === 'completed') {
|
|
2273
|
+
reviewCompletionForced = true;
|
|
2274
|
+
}
|
|
2275
|
+
else {
|
|
2276
|
+
reviewInactivityTimeoutForced = true;
|
|
2277
|
+
}
|
|
2278
|
+
await interruptReviewProcess(reviewProcessState, child, 'SIGTERM');
|
|
2279
|
+
reviewKillTimer = setTimeout(() => {
|
|
2280
|
+
void interruptReviewProcess(reviewProcessState, child, 'SIGKILL');
|
|
2281
|
+
}, REVIEW_AGENT_KILL_GRACE_MS);
|
|
2282
|
+
reviewKillTimer.unref?.();
|
|
2283
|
+
},
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2210
2286
|
child.stdout?.on('data', (chunk) => {
|
|
2287
|
+
lastOutputAt = Date.now();
|
|
2211
2288
|
const text = remainder + chunk.toString();
|
|
2212
2289
|
const lines = text.split('\n');
|
|
2213
2290
|
remainder = lines.pop() ?? '';
|
|
@@ -2230,13 +2307,19 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2230
2307
|
}
|
|
2231
2308
|
const parsed = parseOpencodeJsonLine(line);
|
|
2232
2309
|
const { logFragment, responseFragment, subagentEvents, providerSessionId } = parsed;
|
|
2310
|
+
const hasExplicitFinalAnswer = parsed.recoveryEvents?.some(event => event.type === 'final_answer') === true;
|
|
2233
2311
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
2234
2312
|
executionCapableEventObserved = true;
|
|
2313
|
+
reviewFinalAnswerObserved = hasExplicitFinalAnswer;
|
|
2314
|
+
}
|
|
2315
|
+
else if (hasExplicitFinalAnswer) {
|
|
2316
|
+
reviewFinalAnswerObserved = true;
|
|
2235
2317
|
}
|
|
2236
|
-
bindResolvedOpencodeSession(providerSessionId);
|
|
2318
|
+
bindResolvedOpencodeSession(providerSessionId, true);
|
|
2237
2319
|
subagentTracker.handle(subagentEvents);
|
|
2238
2320
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
2239
2321
|
firstFinalAnswerTime = null;
|
|
2322
|
+
reviewFinalAnswerObserved = false;
|
|
2240
2323
|
}
|
|
2241
2324
|
if (logFragment !== null || responseFragment !== null) {
|
|
2242
2325
|
progressUpdater.schedule();
|
|
@@ -2256,6 +2339,11 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2256
2339
|
}
|
|
2257
2340
|
child.on('close', async (code) => {
|
|
2258
2341
|
progressUpdater.cancel();
|
|
2342
|
+
reviewWatchdog?.stop();
|
|
2343
|
+
if (reviewKillTimer) {
|
|
2344
|
+
clearTimeout(reviewKillTimer);
|
|
2345
|
+
reviewKillTimer = null;
|
|
2346
|
+
}
|
|
2259
2347
|
const finalPlacementFailure = await placementCompletion;
|
|
2260
2348
|
if (finalPlacementFailure || settled) {
|
|
2261
2349
|
return;
|
|
@@ -2266,34 +2354,82 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2266
2354
|
const { subagentEvents, providerSessionId } = parsed;
|
|
2267
2355
|
if (isExecutionCapableParsedStreamLine(parsed)) {
|
|
2268
2356
|
executionCapableEventObserved = true;
|
|
2357
|
+
reviewFinalAnswerObserved = false;
|
|
2269
2358
|
}
|
|
2270
|
-
bindResolvedOpencodeSession(providerSessionId);
|
|
2359
|
+
bindResolvedOpencodeSession(providerSessionId, true);
|
|
2271
2360
|
subagentTracker.handle(subagentEvents);
|
|
2272
2361
|
if (appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker })) {
|
|
2273
2362
|
firstFinalAnswerTime = null;
|
|
2274
2363
|
}
|
|
2275
2364
|
}
|
|
2276
|
-
const
|
|
2365
|
+
const collectCloseEnrichment = async () => {
|
|
2366
|
+
const resolvedSessionOnClose = await attemptOpencodeSessionLookup();
|
|
2367
|
+
await opencodeSubagentMonitor?.refresh();
|
|
2368
|
+
const hasPersistedCompletion = Boolean(opencodeSessionOperations
|
|
2369
|
+
&& hasTrustedOpencodeSessionId
|
|
2370
|
+
&& sessionId
|
|
2371
|
+
&& spawnTimestamp !== null
|
|
2372
|
+
&& await opencodeSessionOperations.hasCompletedAssistantSince(sessionId, spawnTimestamp));
|
|
2373
|
+
const persistedTranscript = hasPersistedCompletion && opencodeSessionOperations && sessionId
|
|
2374
|
+
? await readOpencodeSessionTranscript({
|
|
2375
|
+
sessionId,
|
|
2376
|
+
since: spawnTimestamp ?? undefined,
|
|
2377
|
+
cwd,
|
|
2378
|
+
sessionOperations: opencodeSessionOperations,
|
|
2379
|
+
})
|
|
2380
|
+
: null;
|
|
2381
|
+
return { resolvedSessionOnClose, hasPersistedCompletion, persistedTranscript };
|
|
2382
|
+
};
|
|
2383
|
+
const closeEnrichmentFallback = {
|
|
2384
|
+
resolvedSessionOnClose: hasResolvedOpencodeSessionId,
|
|
2385
|
+
hasPersistedCompletion: false,
|
|
2386
|
+
persistedTranscript: null,
|
|
2387
|
+
};
|
|
2388
|
+
const closeEnrichment = reviewProcessState
|
|
2389
|
+
? await resolveWithin(collectCloseEnrichment(), REVIEW_AGENT_CLOSE_ENRICHMENT_TIMEOUT_MS, closeEnrichmentFallback)
|
|
2390
|
+
: await collectCloseEnrichment();
|
|
2391
|
+
const { resolvedSessionOnClose, hasPersistedCompletion, persistedTranscript } = closeEnrichment;
|
|
2277
2392
|
if (sessionLookupTimer && (resolvedSessionOnClose || hasResolvedOpencodeSessionId)) {
|
|
2278
2393
|
clearTimeout(sessionLookupTimer);
|
|
2279
2394
|
sessionLookupTimer = null;
|
|
2280
2395
|
}
|
|
2281
2396
|
const stderrOutput = Buffer.concat(stderrChunks).toString();
|
|
2282
|
-
|
|
2283
|
-
const log = logLines.join('\n\n') || stderrOutput;
|
|
2397
|
+
const log = logLines.join('\n\n') || persistedTranscript?.log || stderrOutput;
|
|
2284
2398
|
const recoveredResponse = recoveryTracker.getRecoveredResponse();
|
|
2285
|
-
const
|
|
2399
|
+
const streamedResponse = recoveryTracker.hasRecoveredFinalAnswer()
|
|
2286
2400
|
? recoveredResponse
|
|
2287
|
-
: responseLines.join('\n\n')
|
|
2401
|
+
: responseLines.join('\n\n');
|
|
2402
|
+
const persistedResponse = persistedTranscript?.response?.trim() ?? '';
|
|
2403
|
+
const persistedFinalResponse = persistedTranscript?.finalResponse?.trim() ?? '';
|
|
2404
|
+
const selectedPersistedResponse = reviewProcessState
|
|
2405
|
+
? persistedFinalResponse || (!streamedResponse ? persistedResponse : '')
|
|
2406
|
+
: persistedResponse;
|
|
2407
|
+
const finalResponse = selectedPersistedResponse && (reviewCompletionForced || hasPersistedCompletion || !streamedResponse)
|
|
2408
|
+
? selectedPersistedResponse
|
|
2409
|
+
: streamedResponse || stderrOutput;
|
|
2410
|
+
const transcriptResponseText = reviewProcessState && persistedResponse
|
|
2411
|
+
? persistedResponse
|
|
2412
|
+
: finalResponse;
|
|
2288
2413
|
const stderrModelError = parseOpencodeStderrModelResolutionError(stderrOutput);
|
|
2289
2414
|
const timing = buildResponseTiming({ startTime, firstFinalAnswerTime, isStopped: Boolean(buildState?.stopRequested || reviewProcessState?.stopRequested) });
|
|
2290
2415
|
if (buildState?.stopRequested || reviewProcessState?.stopRequested) {
|
|
2291
|
-
const transcriptRuns = finalizeRun('stopped', log, finalResponse);
|
|
2416
|
+
const transcriptRuns = finalizeRun('stopped', log, finalResponse, { transcriptResponseText });
|
|
2292
2417
|
resolveOnce(withTranscriptRuns({ response: finalResponse, responseTiming: timing, usageSnapshot }, transcriptRuns));
|
|
2293
2418
|
return;
|
|
2294
2419
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2420
|
+
const hasUsableReviewResponse = Boolean((persistedResponse || streamedResponse).trim());
|
|
2421
|
+
if (reviewInactivityTimeoutForced) {
|
|
2422
|
+
const message = `Review agent produced no output for ${REVIEW_AGENT_INACTIVITY_TIMEOUT_MS} ms.`;
|
|
2423
|
+
const transcriptRuns = finalizeRun('error', log || message, finalResponse || null, { transcriptResponseText });
|
|
2424
|
+
rejectOnce(attachTranscriptRunsToError(new Error(message), transcriptRuns));
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
const forcedReviewCompletion = Boolean(reviewProcessState
|
|
2428
|
+
&& hasUsableReviewResponse
|
|
2429
|
+
&& !recoveryTracker.hasUnrecoveredError()
|
|
2430
|
+
&& reviewCompletionForced);
|
|
2431
|
+
if (isSuccessfulOpencodeExit(code, stderrModelError, recoveryTracker) || forcedReviewCompletion) {
|
|
2432
|
+
const transcriptRuns = finalizeRun('done', log, finalResponse, { emit: emitSuccessUpdate, transcriptResponseText });
|
|
2297
2433
|
resolveOnce(withTranscriptRuns({ response: finalResponse, responseTiming: timing, usageSnapshot }, transcriptRuns));
|
|
2298
2434
|
return;
|
|
2299
2435
|
}
|
|
@@ -2313,27 +2449,51 @@ const runAgentOnce = ({ ticketId, tool, prompt, cwd, io, model, variant = null,
|
|
|
2313
2449
|
});
|
|
2314
2450
|
child.on('error', err => {
|
|
2315
2451
|
progressUpdater.cancel();
|
|
2452
|
+
reviewWatchdog?.stop();
|
|
2453
|
+
if (reviewKillTimer) {
|
|
2454
|
+
clearTimeout(reviewKillTimer);
|
|
2455
|
+
reviewKillTimer = null;
|
|
2456
|
+
}
|
|
2457
|
+
if (remainder.trim()) {
|
|
2458
|
+
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2459
|
+
const parsed = parseOpencodeJsonLine(remainder);
|
|
2460
|
+
bindResolvedOpencodeSession(parsed.providerSessionId, true);
|
|
2461
|
+
subagentTracker.handle(parsed.subagentEvents);
|
|
2462
|
+
appendOpencodeTranscriptLine({ parsed, logLines, responseLines, recoveryTracker });
|
|
2463
|
+
}
|
|
2464
|
+
const stderrOutput = Buffer.concat(stderrChunks).toString();
|
|
2465
|
+
const log = logLines.join('\n\n') || stderrOutput;
|
|
2466
|
+
const response = recoveryTracker.hasRecoveredFinalAnswer()
|
|
2467
|
+
? recoveryTracker.getRecoveredResponse()
|
|
2468
|
+
: responseLines.join('\n\n') || stderrOutput;
|
|
2316
2469
|
if (sessionLookupTimer && hasResolvedOpencodeSessionId) {
|
|
2317
2470
|
clearTimeout(sessionLookupTimer);
|
|
2318
2471
|
sessionLookupTimer = null;
|
|
2319
2472
|
}
|
|
2320
2473
|
if (buildState?.stopRequested || reviewProcessState?.stopRequested) {
|
|
2321
|
-
const transcriptRuns = finalizeRun('stopped',
|
|
2322
|
-
resolveOnce(withTranscriptRuns({ response
|
|
2474
|
+
const transcriptRuns = finalizeRun('stopped', log, response);
|
|
2475
|
+
resolveOnce(withTranscriptRuns({ response, responseTiming: null, usageSnapshot }, transcriptRuns));
|
|
2323
2476
|
return;
|
|
2324
2477
|
}
|
|
2325
2478
|
const resolvedError = agentProvider.classifyExecutionFailure(err);
|
|
2326
|
-
const transcriptRuns = finalizeRun('error', getErrorMessage(resolvedError),
|
|
2479
|
+
const transcriptRuns = finalizeRun('error', log || getErrorMessage(resolvedError), response);
|
|
2327
2480
|
rejectOnce(attachTranscriptRunsToError(resolvedError, transcriptRuns));
|
|
2328
2481
|
});
|
|
2482
|
+
if (reviewProcessState?.stopRequested) {
|
|
2483
|
+
void interruptReviewProcess(reviewProcessState, child, 'SIGTERM');
|
|
2484
|
+
}
|
|
2485
|
+
else {
|
|
2486
|
+
reviewWatchdog?.start();
|
|
2487
|
+
}
|
|
2329
2488
|
});
|
|
2330
2489
|
};
|
|
2331
2490
|
export const runAgent = (options) => {
|
|
2332
2491
|
const run = () => runAgentOnce(options);
|
|
2333
2492
|
return options.tool === 'opencode' ? retryOpenCodeDatabaseLocks(run) : run();
|
|
2334
2493
|
};
|
|
2335
|
-
const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, opencodeAgentMode = 'build', executionContext, mainRun, spawnPermitId, }) => {
|
|
2494
|
+
const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, opencodeAgentMode = 'build', executionContext, mainRun, spawnPermitId, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
2336
2495
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
2496
|
+
const opencodeSessionOperations = executionProvider.sessionOperations?.opencode ?? null;
|
|
2337
2497
|
const subagentTracker = createSubagentRunTracker({
|
|
2338
2498
|
parentType: 'chat',
|
|
2339
2499
|
parentId: chatSessionId,
|
|
@@ -2375,15 +2535,20 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2375
2535
|
}, transcriptRuns));
|
|
2376
2536
|
return;
|
|
2377
2537
|
}
|
|
2378
|
-
const
|
|
2379
|
-
?
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2538
|
+
const agentProvider = tool === 'claude'
|
|
2539
|
+
? claudeAgentProvider
|
|
2540
|
+
: opencodeAgentProvider;
|
|
2541
|
+
const invocation = agentProvider.buildStreamingInvocation({
|
|
2542
|
+
cwd,
|
|
2543
|
+
files,
|
|
2544
|
+
prompt,
|
|
2545
|
+
model: resolvedModel,
|
|
2546
|
+
variant,
|
|
2547
|
+
mode: opencodeAgentMode,
|
|
2548
|
+
resumeSessionId,
|
|
2549
|
+
sessionId: randomUUID(),
|
|
2550
|
+
});
|
|
2551
|
+
sessionId = invocation.sessionId;
|
|
2387
2552
|
updateChatSessionStatus(chatSessionId, 'running', null, io);
|
|
2388
2553
|
subagentTracker.updateMain(null, null);
|
|
2389
2554
|
if (tool === 'claude' && sessionId) {
|
|
@@ -2397,6 +2562,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2397
2562
|
io,
|
|
2398
2563
|
spawnTimestamp,
|
|
2399
2564
|
initialSessionId: resumeSessionId,
|
|
2565
|
+
...(opencodeSessionOperations ? { sessionOperations: opencodeSessionOperations } : {}),
|
|
2400
2566
|
});
|
|
2401
2567
|
chatState.metadataRefresher = opencodeMetadataRefresher;
|
|
2402
2568
|
if (resumeSessionId) {
|
|
@@ -2409,6 +2575,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2409
2575
|
}
|
|
2410
2576
|
}
|
|
2411
2577
|
let child;
|
|
2578
|
+
let placementCompletion;
|
|
2412
2579
|
try {
|
|
2413
2580
|
if (chatState.stopRequested) {
|
|
2414
2581
|
throw new AgentSpawnPermitRevokedError();
|
|
@@ -2416,27 +2583,40 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2416
2583
|
if (spawnPermitId) {
|
|
2417
2584
|
assertAgentSpawnPermit(spawnPermitId, executionContext.runId);
|
|
2418
2585
|
}
|
|
2419
|
-
|
|
2586
|
+
const handle = executionProvider.execute({
|
|
2587
|
+
phase: 'agent',
|
|
2588
|
+
target: executionTarget ?? null,
|
|
2589
|
+
command: invocation.command,
|
|
2590
|
+
args: invocation.args,
|
|
2420
2591
|
cwd,
|
|
2421
|
-
stdio:
|
|
2422
|
-
|
|
2592
|
+
stdio: invocation.stdio,
|
|
2593
|
+
launcherEnvironment: executionLauncherEnvironment,
|
|
2594
|
+
runtimeEnvironment: executionRuntimeEnvironment,
|
|
2423
2595
|
detached: process.platform !== 'win32',
|
|
2424
2596
|
});
|
|
2597
|
+
child = handle.child;
|
|
2598
|
+
chatState.interrupt = handle.interrupt;
|
|
2599
|
+
placementCompletion = handle.placementFailure;
|
|
2425
2600
|
}
|
|
2426
2601
|
catch (error) {
|
|
2427
|
-
const resolvedError =
|
|
2602
|
+
const resolvedError = agentProvider.classifyExecutionFailure(error);
|
|
2428
2603
|
const transcriptRuns = subagentTracker.finalize('error', resolvedError.message, null);
|
|
2429
2604
|
rejectOnce(attachTranscriptRunsToError(resolvedError, transcriptRuns));
|
|
2430
2605
|
return;
|
|
2431
2606
|
}
|
|
2432
2607
|
chatState.child = child;
|
|
2433
2608
|
activeChatProcesses.set(chatSessionId, chatState);
|
|
2434
|
-
if (
|
|
2435
|
-
|
|
2436
|
-
? appendClaudeHeadlessPlanPrompt(prompt)
|
|
2437
|
-
: prompt;
|
|
2438
|
-
writeClaudeStdin(child, buildClaudeStdinMessage(claudePrompt, files, cwd));
|
|
2609
|
+
if (invocation.stdin !== null) {
|
|
2610
|
+
writeClaudeStdin(child, invocation.stdin);
|
|
2439
2611
|
}
|
|
2612
|
+
void placementCompletion.then(failure => {
|
|
2613
|
+
if (!failure || settled) {
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
const resolvedError = agentProvider.classifyExecutionFailure(failure);
|
|
2617
|
+
const transcriptRuns = subagentTracker.finalize('error', resolvedError.message, null);
|
|
2618
|
+
rejectOnce(attachTranscriptRunsToError(resolvedError, transcriptRuns));
|
|
2619
|
+
});
|
|
2440
2620
|
startTime = Date.now();
|
|
2441
2621
|
if (tool === 'opencode' && spawnTimestamp !== null) {
|
|
2442
2622
|
opencodeSubagentMonitor = createOpencodeSubagentMonitor({
|
|
@@ -2444,6 +2624,9 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2444
2624
|
spawnTimestamp,
|
|
2445
2625
|
cwd,
|
|
2446
2626
|
tracker: subagentTracker,
|
|
2627
|
+
taskSessionReader: opencodeSessionOperations
|
|
2628
|
+
? createOpencodeTaskSessionReader(opencodeSessionOperations)
|
|
2629
|
+
: null,
|
|
2447
2630
|
discoverParentSession: false,
|
|
2448
2631
|
onParentSessionId: discoveredSessionId => {
|
|
2449
2632
|
updateChatSessionId(chatSessionId, 'opencode', discoveredSessionId, io);
|
|
@@ -2487,8 +2670,12 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2487
2670
|
}
|
|
2488
2671
|
});
|
|
2489
2672
|
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
2490
|
-
child.on('close', code => {
|
|
2673
|
+
child.on('close', async (code) => {
|
|
2491
2674
|
progressUpdater.cancel();
|
|
2675
|
+
const placementFailure = await placementCompletion;
|
|
2676
|
+
if (placementFailure || settled) {
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2492
2679
|
if (remainder.trim()) {
|
|
2493
2680
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2494
2681
|
const parsed = parseClaudeStreamJsonLine(remainder);
|
|
@@ -2627,6 +2814,10 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2627
2814
|
}
|
|
2628
2815
|
child.on('close', async (code) => {
|
|
2629
2816
|
progressUpdater.cancel();
|
|
2817
|
+
const placementFailure = await placementCompletion;
|
|
2818
|
+
if (placementFailure || settled) {
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2630
2821
|
if (remainder.trim()) {
|
|
2631
2822
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2632
2823
|
const parsed = parseOpencodeJsonLine(remainder, cwd);
|
|
@@ -2665,6 +2856,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2665
2856
|
current: usageSnapshot,
|
|
2666
2857
|
sessionId: resolvedOpencodeSessionId,
|
|
2667
2858
|
spawnTimestamp,
|
|
2859
|
+
...(opencodeSessionOperations ? { sessionOperations: opencodeSessionOperations } : {}),
|
|
2668
2860
|
});
|
|
2669
2861
|
}
|
|
2670
2862
|
updateChatSessionStatus(chatSessionId, 'running', preservedOutput?.log ?? null, io, preservedOutput?.response ?? null);
|
|
@@ -2689,6 +2881,7 @@ const runChatAgentOnce = ({ chatSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2689
2881
|
current: usageSnapshot,
|
|
2690
2882
|
sessionId: resolvedOpencodeSessionId,
|
|
2691
2883
|
spawnTimestamp,
|
|
2884
|
+
...(opencodeSessionOperations ? { sessionOperations: opencodeSessionOperations } : {}),
|
|
2692
2885
|
});
|
|
2693
2886
|
}
|
|
2694
2887
|
updateChatSessionStatus(chatSessionId, 'running', finalOutput.log, io, finalOutput.response);
|
|
@@ -2746,7 +2939,7 @@ export const runChatAgent = (options) => {
|
|
|
2746
2939
|
const run = () => runChatAgentOnce(options);
|
|
2747
2940
|
return options.tool === 'opencode' ? retryOpenCodeDatabaseLocks(run) : run();
|
|
2748
2941
|
};
|
|
2749
|
-
const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, executionContext, mainRun, }) => {
|
|
2942
|
+
const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, executionContext, mainRun, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
2750
2943
|
const resolvedModel = resolveAgentModel(tool, model);
|
|
2751
2944
|
const subagentTracker = createSubagentRunTracker({
|
|
2752
2945
|
parentType: 'spec',
|
|
@@ -2822,13 +3015,27 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2822
3015
|
}
|
|
2823
3016
|
}
|
|
2824
3017
|
let child;
|
|
3018
|
+
let placementFailure = Promise.resolve(null);
|
|
2825
3019
|
try {
|
|
2826
|
-
|
|
3020
|
+
const handle = executionProvider.execute({
|
|
3021
|
+
phase: 'agent',
|
|
3022
|
+
target: executionTarget ?? null,
|
|
3023
|
+
command: cmd,
|
|
3024
|
+
args,
|
|
2827
3025
|
cwd,
|
|
2828
3026
|
stdio: tool === 'claude' ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
|
|
2829
|
-
|
|
3027
|
+
launcherEnvironment: executionLauncherEnvironment,
|
|
3028
|
+
runtimeEnvironment: executionRuntimeEnvironment,
|
|
2830
3029
|
detached: process.platform !== 'win32',
|
|
2831
3030
|
});
|
|
3031
|
+
child = handle.child;
|
|
3032
|
+
specState.interrupt = handle.interrupt;
|
|
3033
|
+
placementFailure = handle.placementFailure;
|
|
3034
|
+
void placementFailure.then(failure => {
|
|
3035
|
+
if (failure) {
|
|
3036
|
+
rejectOnce(failure);
|
|
3037
|
+
}
|
|
3038
|
+
});
|
|
2832
3039
|
}
|
|
2833
3040
|
catch (error) {
|
|
2834
3041
|
const resolvedError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2887,8 +3094,13 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
2887
3094
|
}
|
|
2888
3095
|
});
|
|
2889
3096
|
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
2890
|
-
child.on('close', code => {
|
|
3097
|
+
child.on('close', async (code) => {
|
|
2891
3098
|
progressUpdater.cancel();
|
|
3099
|
+
const placementError = await placementFailure;
|
|
3100
|
+
if (placementError) {
|
|
3101
|
+
rejectOnce(placementError);
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
2892
3104
|
if (remainder.trim()) {
|
|
2893
3105
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
2894
3106
|
const { logFragment, responseFragment, subagentEvents } = parseClaudeStreamJsonLine(remainder);
|
|
@@ -3016,6 +3228,11 @@ const runSpecAgentOnce = ({ specSessionId, tool, prompt, files = [], cwd, io, mo
|
|
|
3016
3228
|
}
|
|
3017
3229
|
child.on('close', async (code) => {
|
|
3018
3230
|
progressUpdater.cancel();
|
|
3231
|
+
const placementError = await placementFailure;
|
|
3232
|
+
if (placementError) {
|
|
3233
|
+
rejectOnce(placementError);
|
|
3234
|
+
return;
|
|
3235
|
+
}
|
|
3019
3236
|
await opencodeSubagentMonitor?.refresh();
|
|
3020
3237
|
if (remainder.trim()) {
|
|
3021
3238
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
@@ -3141,7 +3358,7 @@ export const runSpecAgent = (options) => {
|
|
|
3141
3358
|
* updateSpecReviewRunStatus / updateSpecReviewRunStreaming / updateSpecReviewRunUsage —
|
|
3142
3359
|
* the spec_sessions row is never touched by this path.
|
|
3143
3360
|
*/
|
|
3144
|
-
const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, executionContext, }) => {
|
|
3361
|
+
const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, executionContext, executionProvider = hostExecutionEnvironment, executionTarget, executionLauncherEnvironment = createHostLauncherEnvironment(), executionRuntimeEnvironment = createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }), }) => {
|
|
3145
3362
|
const registeredState = activeSpecReviewProcesses.get(specReviewRunId);
|
|
3146
3363
|
if (registeredState?.stopRequested) {
|
|
3147
3364
|
return Promise.resolve({
|
|
@@ -3169,6 +3386,7 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3169
3386
|
const reviewState = registeredState ?? {
|
|
3170
3387
|
child: null,
|
|
3171
3388
|
interrupt: null,
|
|
3389
|
+
abortController: new AbortController(),
|
|
3172
3390
|
researchChildren: new Set(),
|
|
3173
3391
|
stopRequested: false,
|
|
3174
3392
|
metadataRefresher: null,
|
|
@@ -3189,42 +3407,39 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3189
3407
|
buildClaudeArgs('plan', resolvedModel, null, randomUUID()),
|
|
3190
3408
|
];
|
|
3191
3409
|
updateSpecReviewRunStreaming(specReviewRunId, null);
|
|
3192
|
-
const env = createWorkspaceProcessEnvironment(cwd, { PWD: cwd });
|
|
3193
3410
|
let child;
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
command: invocation.command,
|
|
3201
|
-
args: invocation.args,
|
|
3202
|
-
cwd,
|
|
3203
|
-
stdio: invocation.stdio,
|
|
3204
|
-
launcherEnvironment: createHostLauncherEnvironment(),
|
|
3205
|
-
runtimeEnvironment: createLegacyHostRuntimeEnvironment(cwd, { PWD: cwd }),
|
|
3206
|
-
detached: process.platform !== 'win32',
|
|
3207
|
-
});
|
|
3208
|
-
child = handle.child;
|
|
3209
|
-
reviewState.interrupt = handle.interrupt;
|
|
3210
|
-
void handle.placementFailure.then(failure => {
|
|
3211
|
-
if (failure) {
|
|
3212
|
-
rejectOnce(opencodeAgentProvider.classifyExecutionFailure(failure));
|
|
3213
|
-
}
|
|
3214
|
-
});
|
|
3215
|
-
}
|
|
3216
|
-
catch (error) {
|
|
3217
|
-
rejectOnce(opencodeAgentProvider.classifyExecutionFailure(error));
|
|
3218
|
-
return;
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3221
|
-
else {
|
|
3222
|
-
child = spawn(cmd, args, {
|
|
3223
|
-
cwd,
|
|
3411
|
+
let placementFailure = Promise.resolve(null);
|
|
3412
|
+
const invocation = tool === 'opencode'
|
|
3413
|
+
? buildOpencodeSpecReviewInvocation({ prompt, files, model: resolvedModel, variant })
|
|
3414
|
+
: {
|
|
3415
|
+
command: cmd,
|
|
3416
|
+
args,
|
|
3224
3417
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3225
|
-
|
|
3418
|
+
};
|
|
3419
|
+
try {
|
|
3420
|
+
const handle = executionProvider.execute({
|
|
3421
|
+
phase: 'agent',
|
|
3422
|
+
target: executionTarget ?? null,
|
|
3423
|
+
command: invocation.command,
|
|
3424
|
+
args: invocation.args,
|
|
3425
|
+
cwd,
|
|
3426
|
+
stdio: invocation.stdio,
|
|
3427
|
+
launcherEnvironment: executionLauncherEnvironment,
|
|
3428
|
+
runtimeEnvironment: executionRuntimeEnvironment,
|
|
3226
3429
|
detached: process.platform !== 'win32',
|
|
3227
3430
|
});
|
|
3431
|
+
child = handle.child;
|
|
3432
|
+
reviewState.interrupt = handle.interrupt;
|
|
3433
|
+
placementFailure = handle.placementFailure;
|
|
3434
|
+
void placementFailure.then(failure => {
|
|
3435
|
+
if (failure) {
|
|
3436
|
+
rejectOnce(tool === 'opencode' ? opencodeAgentProvider.classifyExecutionFailure(failure) : failure);
|
|
3437
|
+
}
|
|
3438
|
+
});
|
|
3439
|
+
}
|
|
3440
|
+
catch (error) {
|
|
3441
|
+
rejectOnce(tool === 'opencode' ? opencodeAgentProvider.classifyExecutionFailure(error) : error);
|
|
3442
|
+
return;
|
|
3228
3443
|
}
|
|
3229
3444
|
reviewState.child = child;
|
|
3230
3445
|
activeSpecReviewProcesses.set(specReviewRunId, reviewState);
|
|
@@ -3274,8 +3489,13 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3274
3489
|
}
|
|
3275
3490
|
});
|
|
3276
3491
|
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
3277
|
-
child.on('close', code => {
|
|
3492
|
+
child.on('close', async (code) => {
|
|
3278
3493
|
progressUpdater.cancel();
|
|
3494
|
+
const placementError = await placementFailure;
|
|
3495
|
+
if (placementError) {
|
|
3496
|
+
rejectOnce(placementError);
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3279
3499
|
if (remainder.trim()) {
|
|
3280
3500
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|
|
3281
3501
|
const { logFragment, responseFragment, subagentEvents } = parseClaudeStreamJsonLine(remainder);
|
|
@@ -3368,6 +3588,11 @@ const runSpecReviewAgentOnce = ({ specReviewRunId, specSessionId, tool, prompt,
|
|
|
3368
3588
|
child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
|
|
3369
3589
|
child.on('close', async (code) => {
|
|
3370
3590
|
progressUpdater.cancel();
|
|
3591
|
+
const placementError = await placementFailure;
|
|
3592
|
+
if (placementError) {
|
|
3593
|
+
rejectOnce(tool === 'opencode' ? opencodeAgentProvider.classifyExecutionFailure(placementError) : placementError);
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3371
3596
|
await opencodeSubagentMonitor?.refresh();
|
|
3372
3597
|
if (remainder.trim()) {
|
|
3373
3598
|
usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
|