@axhub/genie 0.2.11 → 0.2.12
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/api-docs.html +2 -2
- package/dist/assets/App-Clb2COtW.js +274 -0
- package/dist/assets/ImagePlaygroundPage-DqhMSbM8.js +106 -0
- package/dist/assets/ImagePlaygroundPage-MEn3NN80.css +1 -0
- package/dist/assets/ReviewApp-CDcLYe-u.js +1 -0
- package/dist/assets/{_basePickBy-BDnj7-0Z.js → _basePickBy-jUZsM51q.js} +1 -1
- package/dist/assets/{_baseUniq-Bl0JKOyl.js → _baseUniq-BXglE6_v.js} +1 -1
- package/dist/assets/{arc-DY-4Kev3.js → arc-D-oFCFBv.js} +1 -1
- package/dist/assets/{architectureDiagram-2XIMDMQ5-qw7crNVd.js → architectureDiagram-2XIMDMQ5-DC8bAnQt.js} +1 -1
- package/dist/assets/{blockDiagram-WCTKOSBZ-B9xg7ep3.js → blockDiagram-WCTKOSBZ-C4semIRc.js} +1 -1
- package/dist/assets/{c4Diagram-IC4MRINW-H9xp3ytb.js → c4Diagram-IC4MRINW-FHj1QO3y.js} +1 -1
- package/dist/assets/channel-BF4woPXX.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-B3EVDUxI.js → chunk-4BX2VUAB-D-LjsQ_s.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CGv945ef.js → chunk-55IACEB6-DI3j_d7A.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-uAT4CKWM.js → chunk-FMBD7UC4-BEVnaLFN.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-Cbvlpkf7.js → chunk-JSJVCQXG-CSxpcErk.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-CcqIuGat.js → chunk-KX2RTZJC-BbuhDN4h.js} +1 -1
- package/dist/assets/{chunk-NQ4KR5QH-CgrcsRuX.js → chunk-NQ4KR5QH-C3x61XQa.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Cx0APOoV.js → chunk-QZHKN3VN-DxWOFtPh.js} +1 -1
- package/dist/assets/{chunk-WL4C6EOR-BbZirvBk.js → chunk-WL4C6EOR-Bt2OauD2.js} +1 -1
- package/dist/assets/classDiagram-VBA2DB6C-D2kHlnQ7.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-D2kHlnQ7.js +1 -0
- package/dist/assets/clone-CqBvwCJW.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CrvmGFLD.js → cose-bilkent-S5V4N54A-Dexadrue.js} +1 -1
- package/dist/assets/{dagre-KLK3FWXG-C-W6VPjS.js → dagre-KLK3FWXG-F9U4X2xC.js} +1 -1
- package/dist/assets/{diagram-E7M64L7V-IP2q3bL0.js → diagram-E7M64L7V-B3V17aH3.js} +1 -1
- package/dist/assets/{diagram-IFDJBPK2-CQaL-XyV.js → diagram-IFDJBPK2-CdHAmLL1.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-BxBLThfv.js → diagram-P4PSJMXO-CrTNfk8K.js} +1 -1
- package/dist/assets/{erDiagram-INFDFZHY-Dyl7bJTt.js → erDiagram-INFDFZHY-vDh9SWK9.js} +1 -1
- package/dist/assets/{flowDiagram-PKNHOUZH-B7NFMgFK.js → flowDiagram-PKNHOUZH-DpltMg7L.js} +1 -1
- package/dist/assets/{ganttDiagram-A5KZAMGK-hReWSDu2.js → ganttDiagram-A5KZAMGK-COTk2xur.js} +1 -1
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-gVgcr0ST.js → gitGraphDiagram-K3NZZRJ6-BNV7bvvj.js} +1 -1
- package/dist/assets/{graph-DNDiJhTn.js → graph-Dkeg9oys.js} +1 -1
- package/dist/assets/{highlighted-body-TPN3WLV5-DclLmTou.js → highlighted-body-TPN3WLV5-DaiQEBwR.js} +1 -1
- package/dist/assets/index-DgGmiqsP.css +1 -0
- package/dist/assets/index-DvA901Vs.js +2 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-CqQOOzDA.js → infoDiagram-LFFYTUFH-CZioW3Gt.js} +1 -1
- package/dist/assets/{ishikawaDiagram-PHBUUO56-CZ0iLiHg.js → ishikawaDiagram-PHBUUO56-BbqR3i1B.js} +1 -1
- package/dist/assets/{journeyDiagram-4ABVD52K-DdfYKfNh.js → journeyDiagram-4ABVD52K-wfb-WHzl.js} +1 -1
- package/dist/assets/{kanban-definition-K7BYSVSG-C5Vf32u6.js → kanban-definition-K7BYSVSG-B3c4y3VN.js} +1 -1
- package/dist/assets/{layout-rvTEu2KS.js → layout-Xr9Z2VGF.js} +1 -1
- package/dist/assets/{linear-CD9SiYze.js → linear-JBmzAJtl.js} +1 -1
- package/dist/assets/{mermaid-O7DHMXV3-OZ8qWWwa.js → mermaid-O7DHMXV3-fDuyNLKe.js} +230 -222
- package/dist/assets/{mindmap-definition-YRQLILUH-CQxrLNVc.js → mindmap-definition-YRQLILUH-B5NTN_jD.js} +1 -1
- package/dist/assets/{pieDiagram-SKSYHLDU-XgAUByWg.js → pieDiagram-SKSYHLDU-CuO98GVu.js} +1 -1
- package/dist/assets/{quadrantDiagram-337W2JSQ-CH16ls7G.js → quadrantDiagram-337W2JSQ-LL3f4vLf.js} +1 -1
- package/dist/assets/{requirementDiagram-Z7DCOOCP-B_kQO06L.js → requirementDiagram-Z7DCOOCP-Di-2O6LH.js} +1 -1
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-ofe78CyS.js → sankeyDiagram-WA2Y5GQK-9lHqrXqR.js} +1 -1
- package/dist/assets/{sequenceDiagram-2WXFIKYE-Ckbxwny6.js → sequenceDiagram-2WXFIKYE-BQu-SoGr.js} +1 -1
- package/dist/assets/{stateDiagram-RAJIS63D-DNtzCk14.js → stateDiagram-RAJIS63D-BUxvd2BC.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-CDVexTiR.js +1 -0
- package/dist/assets/{timeline-definition-YZTLITO2-zT6CklKt.js → timeline-definition-YZTLITO2-oP47UEU6.js} +1 -1
- package/dist/assets/{treemap-KZPCXAKY-y0U2c3xG.js → treemap-KZPCXAKY-BRjDo2aE.js} +1 -1
- package/dist/assets/{vendor-codemirror-CMHSJ_9p.js → vendor-codemirror-BiCeS-y4.js} +1 -1
- package/dist/assets/{vendor-react-xmA_f8ig.js → vendor-react-DVlYPmi3.js} +1 -1
- package/dist/assets/{vennDiagram-LZ73GAT5-xKj3SjYG.js → vennDiagram-LZ73GAT5-DrRqcDqo.js} +1 -1
- package/dist/assets/{xychartDiagram-JWTSCODW-Da_qyEoX.js → xychartDiagram-JWTSCODW-DUXrymAi.js} +1 -1
- package/dist/index.html +4 -4
- package/package.json +25 -6
- package/scripts/refresh-acp-default-capabilities.mjs +160 -0
- package/server/acp-runtime/client.js +1137 -181
- package/server/acp-runtime/command-overrides.js +48 -0
- package/server/acp-runtime/index.js +576 -16
- package/server/acp-runtime/registry.js +6 -4
- package/server/acp-runtime/session-store.js +235 -92
- package/server/database/db.js +12 -3
- package/server/external-agent/ws.js +212 -11
- package/server/index.js +145 -52
- package/server/projects-watcher-config.js +4 -0
- package/server/projects.js +466 -125
- package/server/routes/cc-connect.js +5 -4
- package/server/routes/codex.js +24 -0
- package/server/routes/commands.js +144 -1
- package/server/routes/runs.js +641 -0
- package/server/routes/session-core.js +357 -109
- package/server/session-core/eventStore.js +0 -121
- package/server/session-core/providerAdapters.js +644 -163
- package/server/session-core/providerDiscovery.js +66 -38
- package/server/session-core/runRegistry.js +244 -0
- package/server/session-core/runtimeState.js +75 -3
- package/server/session-core/runtimeWriter.js +132 -10
- package/server/utils/codexImagePlayground.js +479 -0
- package/server/utils/localTerminal.js +56 -0
- package/server/utils/shellCommand.js +70 -0
- package/shared/acpCapabilities.js +393 -0
- package/shared/acpDefaultCapabilities.generated.json +141 -0
- package/shared/conversationEvents.js +425 -121
- package/dist/assets/App-VH1wNUHs.js +0 -259
- package/dist/assets/ReviewApp-D_9EN4TM.js +0 -1
- package/dist/assets/channel-CyNUnRfc.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-DxBtyz2A.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-DxBtyz2A.js +0 -1
- package/dist/assets/clone-C341l3d0.js +0 -1
- package/dist/assets/index-DBkz_W_P.css +0 -1
- package/dist/assets/index-DdRyoXKh.js +0 -2
- package/dist/assets/stateDiagram-v2-FVOUBMTO-B3VPhiE1.js +0 -1
|
@@ -118,6 +118,151 @@ function normalizePositiveInteger(value) {
|
|
|
118
118
|
return next;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
function readContextCurrentFilePath(context) {
|
|
122
|
+
if (!isPlainObject(context)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (typeof context.currentFile === 'string') {
|
|
127
|
+
return normalizeNonEmptyString(context.currentFile);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (isPlainObject(context.currentFile)) {
|
|
131
|
+
return normalizeNonEmptyString(context.currentFile.path);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (isPlainObject(context.extensions)) {
|
|
135
|
+
return normalizeNonEmptyString(context.extensions.targetPath);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizePathValue(value) {
|
|
142
|
+
const normalized = normalizeNonEmptyString(value);
|
|
143
|
+
if (!normalized) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return normalized
|
|
147
|
+
.replace(/\\/g, '/')
|
|
148
|
+
.replace(/^\.\//, '');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function pathsReferToSameFile(left, right) {
|
|
152
|
+
const normalizedLeft = normalizePathValue(left);
|
|
153
|
+
const normalizedRight = normalizePathValue(right);
|
|
154
|
+
if (!normalizedLeft || !normalizedRight) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (normalizedLeft === normalizedRight) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return (
|
|
161
|
+
normalizedLeft.endsWith(`/${normalizedRight}`)
|
|
162
|
+
|| normalizedRight.endsWith(`/${normalizedLeft}`)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function readContextCurrentFilePathFromPageUrl(pageUrl) {
|
|
167
|
+
const normalizedPageUrl = normalizeNonEmptyString(pageUrl);
|
|
168
|
+
if (!normalizedPageUrl) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const url = new URL(normalizedPageUrl, 'http://localhost');
|
|
174
|
+
const targetPath = normalizePathValue(url.searchParams.get('targetPath'));
|
|
175
|
+
if (targetPath) {
|
|
176
|
+
return targetPath;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const rawContext = normalizeNonEmptyString(url.searchParams.get('context'));
|
|
180
|
+
if (!rawContext) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return readContextCurrentFilePath(JSON.parse(rawContext));
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getIntegrationConnectionRecency(meta) {
|
|
191
|
+
return Math.max(Date.parse(meta?.lastSeenAt || '') || 0, Date.parse(meta?.connectedAt || '') || 0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getIntegrationConnectionIdentityKey(meta) {
|
|
195
|
+
if (!meta?.role || !meta?.channel || !meta?.clientId) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return `${meta.role}::${meta.channel}::${meta.clientId}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getIntegrationPageIdentityKey(meta) {
|
|
202
|
+
const pageUrl = normalizeNonEmptyString(meta?.pageUrl);
|
|
203
|
+
if (!meta?.role || !meta?.channel || !pageUrl) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return `${meta.role}::${meta.channel}::${pageUrl}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function pickMostRecentIntegrationTarget(targets) {
|
|
210
|
+
return targets.slice().sort((left, right) => {
|
|
211
|
+
return getIntegrationConnectionRecency(right) - getIntegrationConnectionRecency(left);
|
|
212
|
+
})[0] || null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function dedupeIntegrationConnectionsByIdentity(connections) {
|
|
216
|
+
const deduped = new Map();
|
|
217
|
+
|
|
218
|
+
for (const meta of connections) {
|
|
219
|
+
const key = getIntegrationConnectionIdentityKey(meta);
|
|
220
|
+
if (!key) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const previous = deduped.get(key);
|
|
225
|
+
if (!previous || getIntegrationConnectionRecency(meta) >= getIntegrationConnectionRecency(previous)) {
|
|
226
|
+
deduped.set(key, meta);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return Array.from(deduped.values());
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function collapseDuplicateFrontendPagesByPageUrl(connections) {
|
|
234
|
+
const deduped = new Map();
|
|
235
|
+
const withoutPageUrl = [];
|
|
236
|
+
|
|
237
|
+
for (const meta of connections) {
|
|
238
|
+
const key = getIntegrationPageIdentityKey(meta);
|
|
239
|
+
if (!key) {
|
|
240
|
+
withoutPageUrl.push(meta);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const previous = deduped.get(key);
|
|
245
|
+
if (!previous || getIntegrationConnectionRecency(meta) >= getIntegrationConnectionRecency(previous)) {
|
|
246
|
+
deduped.set(key, meta);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return [...deduped.values(), ...withoutPageUrl];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function pickContextMatchedTarget(targets, context) {
|
|
254
|
+
const currentFilePath = readContextCurrentFilePath(context);
|
|
255
|
+
if (!currentFilePath) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const matches = targets.filter((target) => (
|
|
260
|
+
pathsReferToSameFile(readContextCurrentFilePathFromPageUrl(target.pageUrl), currentFilePath)
|
|
261
|
+
));
|
|
262
|
+
|
|
263
|
+
return matches.length > 0 ? pickMostRecentIntegrationTarget(matches) : null;
|
|
264
|
+
}
|
|
265
|
+
|
|
121
266
|
function buildSessionActivitySubscriptionKey({ requestId = null, provider = null, sessionId = null } = {}) {
|
|
122
267
|
if (requestId) {
|
|
123
268
|
return `request:${requestId}`;
|
|
@@ -359,7 +504,7 @@ function buildSessionActivityFromConversationEvent({
|
|
|
359
504
|
id: event.eventId,
|
|
360
505
|
timestamp: event.timestamp,
|
|
361
506
|
kind: 'tool',
|
|
362
|
-
text:
|
|
507
|
+
text: toolName,
|
|
363
508
|
requestId,
|
|
364
509
|
sessionId: resolvedSessionId,
|
|
365
510
|
provider: resolvedProvider,
|
|
@@ -726,8 +871,33 @@ function touchIntegrationConnection(ws) {
|
|
|
726
871
|
return updated;
|
|
727
872
|
}
|
|
728
873
|
|
|
874
|
+
function removeDuplicateIntegrationIdentityConnections(nextMeta, currentWs) {
|
|
875
|
+
if (nextMeta?.role !== INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const nextKey = getIntegrationConnectionIdentityKey(nextMeta);
|
|
880
|
+
if (!nextKey) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
for (const [candidateWs, candidateMeta] of integrationConnections.entries()) {
|
|
885
|
+
if (candidateWs === currentWs) {
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (getIntegrationConnectionIdentityKey(candidateMeta) !== nextKey) {
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
integrationConnections.delete(candidateWs);
|
|
894
|
+
removePendingIntegrationRequestsForWs(candidateWs);
|
|
895
|
+
removePendingIntegrationPingsForWs(candidateWs);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
729
899
|
function listIntegrationConnections({ role = null, channel = null, clientId = null } = {}) {
|
|
730
|
-
|
|
900
|
+
const matchingConnections = Array.from(integrationConnections.values()).filter((meta) => {
|
|
731
901
|
if (role && meta.role !== role) {
|
|
732
902
|
return false;
|
|
733
903
|
}
|
|
@@ -742,6 +912,12 @@ function listIntegrationConnections({ role = null, channel = null, clientId = nu
|
|
|
742
912
|
|
|
743
913
|
return true;
|
|
744
914
|
});
|
|
915
|
+
|
|
916
|
+
if (role !== INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
917
|
+
return matchingConnections;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return dedupeIntegrationConnectionsByIdentity(matchingConnections);
|
|
745
921
|
}
|
|
746
922
|
|
|
747
923
|
function buildEditorClientSummary(meta) {
|
|
@@ -977,6 +1153,7 @@ function handleIntegrationConnect(ws, data) {
|
|
|
977
1153
|
};
|
|
978
1154
|
|
|
979
1155
|
const previous = integrationConnections.get(ws) || null;
|
|
1156
|
+
removeDuplicateIntegrationIdentityConnections(nextMeta, ws);
|
|
980
1157
|
integrationConnections.set(ws, nextMeta);
|
|
981
1158
|
|
|
982
1159
|
sendJson(ws, {
|
|
@@ -1197,7 +1374,7 @@ function getTargetFrontendConnections(channel, targetClientId) {
|
|
|
1197
1374
|
});
|
|
1198
1375
|
}
|
|
1199
1376
|
|
|
1200
|
-
function resolveForwardTargets(originWs, { channel, targetClientId }) {
|
|
1377
|
+
function resolveForwardTargets(originWs, { channel, targetClientId, context, requiredCapability = null }) {
|
|
1201
1378
|
const explicitTargetId = normalizeNonEmptyString(targetClientId);
|
|
1202
1379
|
if (explicitTargetId) {
|
|
1203
1380
|
return {
|
|
@@ -1207,7 +1384,15 @@ function resolveForwardTargets(originWs, { channel, targetClientId }) {
|
|
|
1207
1384
|
};
|
|
1208
1385
|
}
|
|
1209
1386
|
|
|
1210
|
-
const
|
|
1387
|
+
const allChannelTargets = getTargetFrontendConnections(channel, null);
|
|
1388
|
+
const channelTargets = requiredCapability
|
|
1389
|
+
? allChannelTargets.filter((target) => target.capabilities?.includes(requiredCapability))
|
|
1390
|
+
: allChannelTargets.filter((target) => (
|
|
1391
|
+
!Array.isArray(target.capabilities)
|
|
1392
|
+
|| target.capabilities.length === 0
|
|
1393
|
+
|| target.capabilities.includes('context.update')
|
|
1394
|
+
|| target.capabilities.includes('prompt.update')
|
|
1395
|
+
));
|
|
1211
1396
|
if (channelTargets.length === 0) {
|
|
1212
1397
|
return {
|
|
1213
1398
|
targets: [],
|
|
@@ -1219,17 +1404,18 @@ function resolveForwardTargets(originWs, { channel, targetClientId }) {
|
|
|
1219
1404
|
};
|
|
1220
1405
|
}
|
|
1221
1406
|
|
|
1222
|
-
|
|
1407
|
+
const pageTargets = collapseDuplicateFrontendPagesByPageUrl(channelTargets);
|
|
1408
|
+
if (pageTargets.length === 1) {
|
|
1223
1409
|
return {
|
|
1224
|
-
targets:
|
|
1225
|
-
resolvedTargetClientId:
|
|
1410
|
+
targets: pageTargets,
|
|
1411
|
+
resolvedTargetClientId: pageTargets[0].clientId,
|
|
1226
1412
|
error: null
|
|
1227
1413
|
};
|
|
1228
1414
|
}
|
|
1229
1415
|
|
|
1230
1416
|
const originMeta = integrationConnections.get(originWs) || null;
|
|
1231
1417
|
const sessionMatchedTargets = originMeta?.sessionId
|
|
1232
|
-
?
|
|
1418
|
+
? pageTargets.filter((target) => target.sessionId && target.sessionId === originMeta.sessionId)
|
|
1233
1419
|
: [];
|
|
1234
1420
|
if (sessionMatchedTargets.length === 1) {
|
|
1235
1421
|
return {
|
|
@@ -1240,7 +1426,7 @@ function resolveForwardTargets(originWs, { channel, targetClientId }) {
|
|
|
1240
1426
|
}
|
|
1241
1427
|
|
|
1242
1428
|
const pageMatchedTargets = originMeta?.pageUrl
|
|
1243
|
-
?
|
|
1429
|
+
? pageTargets.filter((target) => target.pageUrl && target.pageUrl === originMeta.pageUrl)
|
|
1244
1430
|
: [];
|
|
1245
1431
|
if (pageMatchedTargets.length === 1) {
|
|
1246
1432
|
return {
|
|
@@ -1250,6 +1436,15 @@ function resolveForwardTargets(originWs, { channel, targetClientId }) {
|
|
|
1250
1436
|
};
|
|
1251
1437
|
}
|
|
1252
1438
|
|
|
1439
|
+
const contextMatchedTarget = pickContextMatchedTarget(pageTargets, context);
|
|
1440
|
+
if (contextMatchedTarget) {
|
|
1441
|
+
return {
|
|
1442
|
+
targets: [contextMatchedTarget],
|
|
1443
|
+
resolvedTargetClientId: contextMatchedTarget.clientId,
|
|
1444
|
+
error: null
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1253
1448
|
return {
|
|
1254
1449
|
targets: [],
|
|
1255
1450
|
resolvedTargetClientId: null,
|
|
@@ -1499,7 +1694,13 @@ function forwardIntegrationMessage(ws, data, { validator, type, awaitResult = fa
|
|
|
1499
1694
|
}
|
|
1500
1695
|
|
|
1501
1696
|
const { channel, targetClientId } = validation.payload;
|
|
1502
|
-
const
|
|
1697
|
+
const requiredCapability = INTEGRATION_EDITOR_CAPABILITIES[type] || null;
|
|
1698
|
+
const resolution = resolveForwardTargets(ws, {
|
|
1699
|
+
channel,
|
|
1700
|
+
targetClientId,
|
|
1701
|
+
context: validation.payload.context,
|
|
1702
|
+
requiredCapability
|
|
1703
|
+
});
|
|
1503
1704
|
if (resolution.error) {
|
|
1504
1705
|
sendIntegrationError(ws, {
|
|
1505
1706
|
requestId,
|
|
@@ -1519,7 +1720,6 @@ function forwardIntegrationMessage(ws, data, { validator, type, awaitResult = fa
|
|
|
1519
1720
|
return;
|
|
1520
1721
|
}
|
|
1521
1722
|
|
|
1522
|
-
const requiredCapability = INTEGRATION_EDITOR_CAPABILITIES[type] || null;
|
|
1523
1723
|
if (requiredCapability) {
|
|
1524
1724
|
const unsupportedTarget = targets.find((target) => !target.capabilities?.includes(requiredCapability));
|
|
1525
1725
|
if (unsupportedTarget) {
|
|
@@ -2014,6 +2214,7 @@ export function handleExternalAgentWebSocketConnection(ws, request) {
|
|
|
2014
2214
|
});
|
|
2015
2215
|
|
|
2016
2216
|
ws.on('close', () => {
|
|
2217
|
+
removeSessionStateSubscriptionsForWs(ws);
|
|
2017
2218
|
removeSessionActivitySubscriptionsForWs(ws);
|
|
2018
2219
|
unregisterIntegrationConnection(ws);
|
|
2019
2220
|
});
|
package/server/index.js
CHANGED
|
@@ -101,9 +101,11 @@ import {
|
|
|
101
101
|
executeAgentPrompt,
|
|
102
102
|
getActiveAgentSessions,
|
|
103
103
|
isAgentSessionActive,
|
|
104
|
+
resolveAgentElicitation,
|
|
104
105
|
resolveAgentPermission,
|
|
105
106
|
setAgentSessionMode
|
|
106
107
|
} from './acp-runtime/index.js';
|
|
108
|
+
import { mergeAgentCommandOverrides } from './acp-runtime/command-overrides.js';
|
|
107
109
|
import authRoutes from './routes/auth.js';
|
|
108
110
|
import commandsRoutes from './routes/commands.js';
|
|
109
111
|
import settingsRoutes from './routes/settings.js';
|
|
@@ -128,8 +130,11 @@ import userRoutes from './routes/user.js';
|
|
|
128
130
|
import codexRoutes from './routes/codex.js';
|
|
129
131
|
import opencodeRoutes from './routes/opencode.js';
|
|
130
132
|
import sessionCoreRoutes from './routes/session-core.js';
|
|
133
|
+
import runsRoutes from './routes/runs.js';
|
|
131
134
|
import { parseCodexTokenCountInfo } from './utils/codexTokenUsage.js';
|
|
132
135
|
import { getConfiguredDefaultProjectPath, resolveWorkingDirectory } from './utils/defaultWorkingDirectory.js';
|
|
136
|
+
import { buildShellCommand } from './utils/shellCommand.js';
|
|
137
|
+
import { openLocalTerminal } from './utils/localTerminal.js';
|
|
133
138
|
import { initializeDatabase, userDb } from './database/db.js';
|
|
134
139
|
import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js';
|
|
135
140
|
import { IS_PLATFORM } from './constants/config.js';
|
|
@@ -138,6 +143,8 @@ import { SessionEventMirrorWriter } from './session-core/runtimeWriter.js';
|
|
|
138
143
|
import { abortAgentSessionWithWriter } from './session-core/abortSession.js';
|
|
139
144
|
import { authenticateExternalWebSocketRequest } from './external-agent/auth.js';
|
|
140
145
|
import { handleExternalAgentWebSocketConnection } from './external-agent/ws.js';
|
|
146
|
+
import { runRegistry } from './session-core/runRegistry.js';
|
|
147
|
+
import { shouldStartProjectsWatcher } from './projects-watcher-config.js';
|
|
141
148
|
|
|
142
149
|
// File system watcher for projects folder
|
|
143
150
|
let projectsWatcher = null;
|
|
@@ -520,6 +527,40 @@ app.use('/api/user', authenticateToken, userRoutes);
|
|
|
520
527
|
app.use('/api/codex', authenticateToken, codexRoutes);
|
|
521
528
|
app.use('/api/opencode', authenticateToken, opencodeRoutes);
|
|
522
529
|
app.use('/api/session-core', authenticateToken, sessionCoreRoutes);
|
|
530
|
+
app.use('/api', authenticateToken, runsRoutes);
|
|
531
|
+
|
|
532
|
+
app.post('/api/local-terminal/session', authenticateToken, async (req, res) => {
|
|
533
|
+
try {
|
|
534
|
+
const provider = req.body?.provider || 'claude';
|
|
535
|
+
const sessionId = typeof req.body?.sessionId === 'string' ? req.body.sessionId.trim() : '';
|
|
536
|
+
const projectPath = resolveWorkingDirectory({ projectPath: req.body?.projectPath });
|
|
537
|
+
|
|
538
|
+
if (!sessionId) {
|
|
539
|
+
return res.status(400).json({ error: 'sessionId is required' });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const command = buildShellCommand({
|
|
543
|
+
projectPath,
|
|
544
|
+
provider,
|
|
545
|
+
sessionId,
|
|
546
|
+
hasSession: true,
|
|
547
|
+
platform: os.platform()
|
|
548
|
+
});
|
|
549
|
+
const terminal = openLocalTerminal({ command });
|
|
550
|
+
|
|
551
|
+
res.json({
|
|
552
|
+
success: true,
|
|
553
|
+
provider,
|
|
554
|
+
sessionId,
|
|
555
|
+
projectPath,
|
|
556
|
+
command,
|
|
557
|
+
terminal
|
|
558
|
+
});
|
|
559
|
+
} catch (error) {
|
|
560
|
+
console.error('[ERROR] Failed to open local terminal:', error);
|
|
561
|
+
res.status(500).json({ error: error.message });
|
|
562
|
+
}
|
|
563
|
+
});
|
|
523
564
|
|
|
524
565
|
// Agent API Routes (uses API key authentication)
|
|
525
566
|
app.use('/api/agent', agentRoutes);
|
|
@@ -651,6 +692,10 @@ app.get('/api/projects', authenticateToken, async (req, res) => {
|
|
|
651
692
|
|
|
652
693
|
app.get('/api/projects/list', authenticateToken, async (req, res) => {
|
|
653
694
|
try {
|
|
695
|
+
if (String(req.query.force || '').toLowerCase() === 'true') {
|
|
696
|
+
clearProjectDirectoryCache();
|
|
697
|
+
clearProviderSessionLookupCaches();
|
|
698
|
+
}
|
|
654
699
|
const projects = await getProjectsList(broadcastProgress);
|
|
655
700
|
res.json(projects);
|
|
656
701
|
} catch (error) {
|
|
@@ -1161,21 +1206,89 @@ function handleChatConnection(ws) {
|
|
|
1161
1206
|
|
|
1162
1207
|
if (agentCommandTypes.includes(data.type)) {
|
|
1163
1208
|
const agentKey = data.type.replace(/-command$/, '');
|
|
1209
|
+
const clientRequestId = data.clientRequestId || data.options?.clientRequestId || null;
|
|
1164
1210
|
console.log(`[DEBUG] ${agentKey} message:`, data.command || '[Continue/Resume]');
|
|
1165
1211
|
console.log('📁 Project:', data.options?.projectPath || data.options?.cwd || 'Unknown');
|
|
1166
1212
|
console.log('🔄 Session:', data.options?.sessionId ? 'Resume' : 'New');
|
|
1167
1213
|
console.log('🤖 Model:', data.options?.model || 'default');
|
|
1168
1214
|
|
|
1169
|
-
|
|
1170
|
-
agentKey,
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1215
|
+
const run = runRegistry.createRun({
|
|
1216
|
+
provider: agentKey,
|
|
1217
|
+
sessionId: data.sessionId || data.options?.sessionId || null,
|
|
1218
|
+
clientRequestId
|
|
1219
|
+
});
|
|
1220
|
+
let sessionWriter = null;
|
|
1221
|
+
const emitRunState = (status, event = {}) => {
|
|
1222
|
+
const resolvedSessionId = event.sessionId ||
|
|
1223
|
+
sessionWriter?.getSessionId?.() ||
|
|
1224
|
+
data.sessionId ||
|
|
1225
|
+
data.options?.sessionId ||
|
|
1226
|
+
null;
|
|
1227
|
+
const updatedRun = runRegistry.updateRun(run.runId, {
|
|
1228
|
+
status,
|
|
1229
|
+
sessionId: resolvedSessionId,
|
|
1230
|
+
clientRequestId
|
|
1231
|
+
}) || runRegistry.getRun(run.runId);
|
|
1232
|
+
const runEvent = runRegistry.appendRunEvent(run.runId, {
|
|
1233
|
+
type: 'run-state',
|
|
1234
|
+
provider: agentKey,
|
|
1235
|
+
sessionId: updatedRun?.sessionId || resolvedSessionId,
|
|
1236
|
+
clientRequestId,
|
|
1237
|
+
event: {
|
|
1238
|
+
status,
|
|
1239
|
+
...event
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
baseWriter.send({
|
|
1243
|
+
type: 'run-state',
|
|
1244
|
+
provider: agentKey,
|
|
1245
|
+
sessionId: updatedRun?.sessionId || resolvedSessionId,
|
|
1246
|
+
clientRequestId,
|
|
1247
|
+
runId: run.runId,
|
|
1248
|
+
runEventSeq: runEvent?.seq || updatedRun?.lastEventSeq || null,
|
|
1249
|
+
run: updatedRun,
|
|
1250
|
+
event: {
|
|
1251
|
+
status,
|
|
1252
|
+
...event
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
sessionWriter = new SessionEventMirrorWriter(baseWriter, agentKey, {
|
|
1258
|
+
runRegistry,
|
|
1259
|
+
runId: run.runId,
|
|
1260
|
+
clientRequestId
|
|
1177
1261
|
});
|
|
1178
|
-
|
|
1262
|
+
emitRunState('queued', { reason: 'accepted' });
|
|
1263
|
+
|
|
1264
|
+
try {
|
|
1265
|
+
emitRunState('running', { reason: 'execution_started' });
|
|
1266
|
+
const agentCommandOverrides = mergeAgentCommandOverrides(
|
|
1267
|
+
process.env.AXHUB_ACP_COMMAND_OVERRIDES,
|
|
1268
|
+
data.options?.agentCommandOverrides
|
|
1269
|
+
);
|
|
1270
|
+
const result = await executeAgentPrompt({
|
|
1271
|
+
agentKey,
|
|
1272
|
+
command: data.command,
|
|
1273
|
+
options: {
|
|
1274
|
+
...(data.options || {}),
|
|
1275
|
+
...(Object.keys(agentCommandOverrides).length > 0 ? { agentCommandOverrides } : {}),
|
|
1276
|
+
clientRequestId
|
|
1277
|
+
},
|
|
1278
|
+
writer: sessionWriter
|
|
1279
|
+
});
|
|
1280
|
+
emitRunState('succeeded', {
|
|
1281
|
+
sessionId: result?.sessionId || sessionWriter.getSessionId?.() || null,
|
|
1282
|
+
stopReason: result?.stopReason || null
|
|
1283
|
+
});
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
emitRunState('failed', {
|
|
1286
|
+
sessionId: sessionWriter.getSessionId?.() || data.options?.sessionId || null,
|
|
1287
|
+
error: error.message
|
|
1288
|
+
});
|
|
1289
|
+
throw error;
|
|
1290
|
+
}
|
|
1291
|
+
} else if (data.type === 'abort-session') {
|
|
1179
1292
|
console.log('[DEBUG] Abort session request:', data.sessionId);
|
|
1180
1293
|
const provider = typeof data.provider === 'string' && data.provider.trim()
|
|
1181
1294
|
? data.provider.trim().toLowerCase()
|
|
@@ -1213,6 +1326,15 @@ function handleChatConnection(ws) {
|
|
|
1213
1326
|
rememberEntry: data.rememberEntry
|
|
1214
1327
|
});
|
|
1215
1328
|
}
|
|
1329
|
+
} else if (data.type === 'elicitation-response') {
|
|
1330
|
+
if (data.requestId) {
|
|
1331
|
+
resolveAgentElicitation(data.requestId, {
|
|
1332
|
+
action: data.action,
|
|
1333
|
+
content: data.content && typeof data.content === 'object' && !Array.isArray(data.content)
|
|
1334
|
+
? data.content
|
|
1335
|
+
: {}
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1216
1338
|
} else if (data.type === 'check-session-status') {
|
|
1217
1339
|
// Check if a specific session is currently processing
|
|
1218
1340
|
const provider = data.provider || 'claude';
|
|
@@ -1350,47 +1472,14 @@ function handleShellConnection(ws) {
|
|
|
1350
1472
|
}));
|
|
1351
1473
|
|
|
1352
1474
|
try {
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
}
|
|
1362
|
-
} else if (provider === 'gemini') {
|
|
1363
|
-
if (os.platform() === 'win32') {
|
|
1364
|
-
if (hasSession && sessionId) {
|
|
1365
|
-
shellCommand = `Set-Location -Path "${projectPath}"; npx -y @google/gemini-cli --resume ${sessionId}`;
|
|
1366
|
-
} else {
|
|
1367
|
-
shellCommand = `Set-Location -Path "${projectPath}"; npx -y @google/gemini-cli`;
|
|
1368
|
-
}
|
|
1369
|
-
} else {
|
|
1370
|
-
if (hasSession && sessionId) {
|
|
1371
|
-
shellCommand = `cd "${projectPath}" && npx -y @google/gemini-cli --resume ${sessionId}`;
|
|
1372
|
-
} else {
|
|
1373
|
-
shellCommand = `cd "${projectPath}" && npx -y @google/gemini-cli`;
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
} else {
|
|
1377
|
-
// Use claude command (default) or initialCommand if provided
|
|
1378
|
-
const command = initialCommand || 'claude';
|
|
1379
|
-
if (os.platform() === 'win32') {
|
|
1380
|
-
if (hasSession && sessionId) {
|
|
1381
|
-
// Try to resume session, but with fallback to new session if it fails
|
|
1382
|
-
shellCommand = `Set-Location -Path "${projectPath}"; claude --resume ${sessionId}; if ($LASTEXITCODE -ne 0) { claude }`;
|
|
1383
|
-
} else {
|
|
1384
|
-
shellCommand = `Set-Location -Path "${projectPath}"; ${command}`;
|
|
1385
|
-
}
|
|
1386
|
-
} else {
|
|
1387
|
-
if (hasSession && sessionId) {
|
|
1388
|
-
shellCommand = `cd "${projectPath}" && claude --resume ${sessionId} || claude`;
|
|
1389
|
-
} else {
|
|
1390
|
-
shellCommand = `cd "${projectPath}" && ${command}`;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1475
|
+
const shellCommand = buildShellCommand({
|
|
1476
|
+
projectPath,
|
|
1477
|
+
provider,
|
|
1478
|
+
sessionId,
|
|
1479
|
+
hasSession,
|
|
1480
|
+
initialCommand,
|
|
1481
|
+
isPlainShell
|
|
1482
|
+
});
|
|
1394
1483
|
|
|
1395
1484
|
console.log('🔧 Executing shell command:', shellCommand);
|
|
1396
1485
|
|
|
@@ -2195,8 +2284,12 @@ async function startServer() {
|
|
|
2195
2284
|
|
|
2196
2285
|
writeRuntimeStatusFile(PORT, isProduction);
|
|
2197
2286
|
|
|
2198
|
-
|
|
2199
|
-
|
|
2287
|
+
if (shouldStartProjectsWatcher()) {
|
|
2288
|
+
// Opt-in only: provider session files are high-churn and expensive to poll.
|
|
2289
|
+
await setupProjectsWatcher();
|
|
2290
|
+
} else {
|
|
2291
|
+
console.log(`${c.info('[INFO]')} Project/session file watcher: disabled; use manual refresh`);
|
|
2292
|
+
}
|
|
2200
2293
|
});
|
|
2201
2294
|
} catch (error) {
|
|
2202
2295
|
console.error('[ERROR] Failed to start server:', error);
|