@axhub/genie 0.2.8 → 0.2.10
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/LICENSE +21 -675
- package/dist/api-docs.html +2 -2
- package/dist/assets/App-CYCCsgwf.js +264 -0
- package/dist/assets/ReviewApp-0srHIXwb.js +1 -0
- package/dist/assets/{_basePickBy-CqJbRZ9y.js → _basePickBy-DVVb07UV.js} +1 -1
- package/dist/assets/{_baseUniq-BS8YH8jO.js → _baseUniq-BtbziL5G.js} +1 -1
- package/dist/assets/{arc-BBmKEN-S.js → arc-BsCC8yBD.js} +1 -1
- package/dist/assets/{architectureDiagram-2XIMDMQ5-N5lcb82R.js → architectureDiagram-2XIMDMQ5-woFp6eNI.js} +1 -1
- package/dist/assets/{blockDiagram-WCTKOSBZ-DTMwHuLn.js → blockDiagram-WCTKOSBZ-ya8VAc2k.js} +1 -1
- package/dist/assets/{c4Diagram-IC4MRINW-BTKlkXI9.js → c4Diagram-IC4MRINW-CY1dZmIZ.js} +1 -1
- package/dist/assets/channel-BMhScXFe.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-DUdoTxAc.js → chunk-4BX2VUAB-CR1lAd74.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-Bm_92xe4.js → chunk-55IACEB6-CP98WcFC.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CGW0g62g.js → chunk-FMBD7UC4-D9c7ijAB.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-DYkTH3w1.js → chunk-JSJVCQXG-DQAGYOn-.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-C9oTlISU.js → chunk-KX2RTZJC-BbTXiDq7.js} +1 -1
- package/dist/assets/{chunk-NQ4KR5QH-CM50ygWP.js → chunk-NQ4KR5QH-BI6AX0dr.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-7dzpYeNJ.js → chunk-QZHKN3VN-DB3V2Ifo.js} +1 -1
- package/dist/assets/{chunk-WL4C6EOR-Cm9nQrsr.js → chunk-WL4C6EOR-DhzTthv6.js} +1 -1
- package/dist/assets/classDiagram-VBA2DB6C-CMIxlWcT.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-CMIxlWcT.js +1 -0
- package/dist/assets/clone-BPqOt4r3.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-Ccp_p0JZ.js → cose-bilkent-S5V4N54A-BQ09ZE2j.js} +1 -1
- package/dist/assets/{dagre-KLK3FWXG-fBwTLUp9.js → dagre-KLK3FWXG-Dc2ueD_R.js} +1 -1
- package/dist/assets/{diagram-E7M64L7V-CeNVmFUp.js → diagram-E7M64L7V-DP-LsQoL.js} +1 -1
- package/dist/assets/{diagram-IFDJBPK2-CtavyLGa.js → diagram-IFDJBPK2-Cg6r42cB.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-CpQTjQwc.js → diagram-P4PSJMXO-aHsfoUZE.js} +1 -1
- package/dist/assets/{erDiagram-INFDFZHY-B8R5vwhd.js → erDiagram-INFDFZHY-qBXJ4aAz.js} +1 -1
- package/dist/assets/{flowDiagram-PKNHOUZH-BvkVVwIQ.js → flowDiagram-PKNHOUZH-D_13emJM.js} +1 -1
- package/dist/assets/{ganttDiagram-A5KZAMGK-DOu3hSNa.js → ganttDiagram-A5KZAMGK-BvIcOLwz.js} +1 -1
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-C7zT67YE.js → gitGraphDiagram-K3NZZRJ6-ad0vvNcU.js} +1 -1
- package/dist/assets/{graph-D11wiwHo.js → graph-CeJCMjan.js} +1 -1
- package/dist/assets/{highlighted-body-TPN3WLV5-Babpthg-.js → highlighted-body-TPN3WLV5-B_novwSz.js} +1 -1
- package/dist/assets/index-C514cLyb.js +2 -0
- package/dist/assets/index-h1DBl_g3.css +1 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-BmA7IpQG.js → infoDiagram-LFFYTUFH-lOxAqb3m.js} +1 -1
- package/dist/assets/{ishikawaDiagram-PHBUUO56-BEquZd3E.js → ishikawaDiagram-PHBUUO56-DIr-51gj.js} +1 -1
- package/dist/assets/{journeyDiagram-4ABVD52K-BfemGz7f.js → journeyDiagram-4ABVD52K-CYcIW0ZU.js} +1 -1
- package/dist/assets/{kanban-definition-K7BYSVSG-CWja3mln.js → kanban-definition-K7BYSVSG-C1ZK616a.js} +1 -1
- package/dist/assets/{layout-BLUNf-PJ.js → layout-CI2RM-v6.js} +1 -1
- package/dist/assets/{linear-DukIV_Xv.js → linear-DE7bISck.js} +1 -1
- package/dist/assets/{mermaid-O7DHMXV3-SgtM28qI.js → mermaid-O7DHMXV3-XxAJo8EK.js} +6 -6
- package/dist/assets/{mindmap-definition-YRQLILUH-4UjqXITU.js → mindmap-definition-YRQLILUH-Dz6EFjmn.js} +1 -1
- package/dist/assets/{pieDiagram-SKSYHLDU-8AxqJd0M.js → pieDiagram-SKSYHLDU-DPpEzUed.js} +1 -1
- package/dist/assets/{quadrantDiagram-337W2JSQ-D60m8V8r.js → quadrantDiagram-337W2JSQ-xdoXNet7.js} +1 -1
- package/dist/assets/{requirementDiagram-Z7DCOOCP-zqh9jBVf.js → requirementDiagram-Z7DCOOCP-DUq8H3CL.js} +1 -1
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-CDZILTLI.js → sankeyDiagram-WA2Y5GQK-CmqEUxRu.js} +1 -1
- package/dist/assets/{sequenceDiagram-2WXFIKYE-7BReFd0L.js → sequenceDiagram-2WXFIKYE-DhtXRNiH.js} +1 -1
- package/dist/assets/{stateDiagram-RAJIS63D-HPTVdIG4.js → stateDiagram-RAJIS63D-Dj0HOlbN.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-C9utf5gv.js +1 -0
- package/dist/assets/{timeline-definition-YZTLITO2-CTVllFgr.js → timeline-definition-YZTLITO2-DUuJzZB5.js} +1 -1
- package/dist/assets/{treemap-KZPCXAKY-BtyxboJZ.js → treemap-KZPCXAKY-DpYBQ0qr.js} +1 -1
- package/dist/assets/vendor-codemirror-CMHSJ_9p.js +9 -0
- package/dist/assets/{vendor-react-Cpt6D04s.js → vendor-react-xmA_f8ig.js} +1 -1
- package/dist/assets/{vennDiagram-LZ73GAT5-D96ZI6Mg.js → vennDiagram-LZ73GAT5-DpePUyOd.js} +1 -1
- package/dist/assets/{xychartDiagram-JWTSCODW-eRk-39YO.js → xychartDiagram-JWTSCODW-Cfp1I4_U.js} +1 -1
- package/dist/index.html +5 -5
- package/package.json +8 -7
- package/server/acp-runtime/client.js +129 -16
- package/server/acp-runtime/index.js +54 -0
- package/server/acp-runtime/registry.js +2 -2
- package/server/acp-runtime/session-store.js +79 -5
- package/server/cli.js +55 -10
- package/server/database/db.js +20 -0
- package/server/external-agent/service.js +24 -6
- package/server/external-agent/ws.js +540 -27
- package/server/index.js +112 -151
- package/server/lan-access/core.js +79 -0
- package/server/lan-access/state.js +102 -0
- package/server/middleware/auth.js +57 -14
- package/server/projects.js +930 -667
- package/server/routes/auth.js +24 -4
- package/server/routes/cli-auth.js +21 -25
- package/server/routes/codex.js +84 -298
- package/server/routes/commands.js +322 -407
- package/server/routes/lan-access.js +231 -0
- package/server/routes/projects.js +154 -158
- package/server/routes/session-core.js +160 -91
- package/server/routes/settings.js +113 -99
- package/server/session-core/eventStore.js +60 -20
- package/server/session-core/providerAdapters.js +75 -38
- package/server/session-core/runtimeState.js +8 -0
- package/server/session-core/sessionListMerge.js +47 -0
- package/shared/conversationEvents.js +174 -15
- package/shared/modelConstants.js +79 -99
- package/dist/assets/App-CTKZtqB1.js +0 -460
- package/dist/assets/ReviewApp-DM6BNAzR.js +0 -1
- package/dist/assets/channel-1oJBvF-0.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-d5TeKFM4.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-d5TeKFM4.js +0 -1
- package/dist/assets/clone-CinxIlEu.js +0 -1
- package/dist/assets/index-DFxzgWoO.js +0 -2
- package/dist/assets/index-YCFGDVKw.css +0 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-DTUf5_gC.js +0 -1
- package/dist/assets/vendor-codemirror-Dz7_EqNA.js +0 -39
- package/server/_legacy-providers/README.md +0 -30
- package/server/_legacy-providers/claude-sdk.js +0 -956
- package/server/_legacy-providers/gemini-cli.js +0 -368
- package/server/_legacy-providers/openai-codex.js +0 -705
- package/server/_legacy-providers/opencode-cli.js +0 -674
- package/server/routes/git.js +0 -1110
- package/server/routes/mcp-utils.js +0 -48
- package/server/routes/mcp.js +0 -536
- package/server/routes/taskmaster.js +0 -1963
- package/server/utils/mcp-detector.js +0 -198
- package/server/utils/taskmaster-websocket.js +0 -129
|
@@ -1,37 +1,61 @@
|
|
|
1
|
+
import fsSync from 'fs';
|
|
1
2
|
import fs from 'fs/promises';
|
|
2
3
|
import os from 'os';
|
|
3
4
|
import path from 'path';
|
|
5
|
+
import readline from 'readline';
|
|
4
6
|
|
|
5
7
|
import {
|
|
6
8
|
CONVERSATION_EVENT_KINDS,
|
|
7
9
|
isConversationEvent
|
|
8
10
|
} from '../../shared/conversationEvents.js';
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
function getMirroredEventStoreRoot() {
|
|
13
|
+
if (process.env.AXHUB_GENIE_SESSION_EVENTS_ROOT) {
|
|
14
|
+
return path.resolve(process.env.AXHUB_GENIE_SESSION_EVENTS_ROOT);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return path.join(os.homedir(), '.axhub-genie', 'session-events');
|
|
18
|
+
}
|
|
11
19
|
const PERSISTED_EVENT_KINDS = new Set([
|
|
20
|
+
CONVERSATION_EVENT_KINDS.USER_MESSAGE,
|
|
21
|
+
CONVERSATION_EVENT_KINDS.ASSISTANT_TEXT_START,
|
|
22
|
+
CONVERSATION_EVENT_KINDS.ASSISTANT_TEXT_DELTA,
|
|
23
|
+
CONVERSATION_EVENT_KINDS.ASSISTANT_TEXT_END,
|
|
12
24
|
CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED,
|
|
13
25
|
CONVERSATION_EVENT_KINDS.ERROR,
|
|
14
26
|
CONVERSATION_EVENT_KINDS.APPROVAL_REQUEST,
|
|
15
27
|
CONVERSATION_EVENT_KINDS.APPROVAL_RESOLVED,
|
|
16
|
-
CONVERSATION_EVENT_KINDS.SYSTEM_NOTICE
|
|
28
|
+
CONVERSATION_EVENT_KINDS.SYSTEM_NOTICE,
|
|
29
|
+
CONVERSATION_EVENT_KINDS.MODE_UPDATE,
|
|
30
|
+
CONVERSATION_EVENT_KINDS.AVAILABLE_COMMANDS_UPDATE,
|
|
31
|
+
CONVERSATION_EVENT_KINDS.CONFIG_OPTION_UPDATE,
|
|
32
|
+
CONVERSATION_EVENT_KINDS.SESSION_INFO_UPDATE,
|
|
33
|
+
CONVERSATION_EVENT_KINDS.USAGE_UPDATE,
|
|
34
|
+
CONVERSATION_EVENT_KINDS.PLAN_UPDATE,
|
|
35
|
+
CONVERSATION_EVENT_KINDS.ARTIFACT_CREATED
|
|
17
36
|
]);
|
|
18
37
|
|
|
19
38
|
function getSessionEventFilePath(provider, sessionId) {
|
|
20
|
-
return path.join(
|
|
39
|
+
return path.join(getMirroredEventStoreRoot(), String(provider || 'claude'), `${sessionId}.jsonl`);
|
|
21
40
|
}
|
|
22
41
|
|
|
23
42
|
function normalizePersistedEvents(events = []) {
|
|
24
43
|
return events.filter((event) => (
|
|
25
44
|
isConversationEvent(event) &&
|
|
26
45
|
event.sessionId &&
|
|
27
|
-
(
|
|
28
|
-
event.extensions?.runtimeSource === 'acp' ||
|
|
29
|
-
event.rawRef?.runtime === 'acp' ||
|
|
30
|
-
PERSISTED_EVENT_KINDS.has(event.kind)
|
|
31
|
-
)
|
|
46
|
+
PERSISTED_EVENT_KINDS.has(event.kind)
|
|
32
47
|
));
|
|
33
48
|
}
|
|
34
49
|
|
|
50
|
+
function extractSerializedEventKind(line) {
|
|
51
|
+
if (typeof line !== 'string' || !line) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const match = line.match(/"kind"\s*:\s*"([^"]+)"/);
|
|
56
|
+
return match?.[1] || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
function sortObjectKeys(value) {
|
|
36
60
|
if (Array.isArray(value)) {
|
|
37
61
|
return value.map(sortObjectKeys);
|
|
@@ -91,19 +115,35 @@ export async function readMirroredConversationEvents(provider, sessionId) {
|
|
|
91
115
|
const filePath = getSessionEventFilePath(provider, sessionId);
|
|
92
116
|
|
|
93
117
|
try {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
const fileStream = fsSync.createReadStream(filePath, { encoding: 'utf8' });
|
|
119
|
+
const rl = readline.createInterface({
|
|
120
|
+
input: fileStream,
|
|
121
|
+
crlfDelay: Infinity
|
|
122
|
+
});
|
|
123
|
+
const events = [];
|
|
124
|
+
|
|
125
|
+
for await (const rawLine of rl) {
|
|
126
|
+
const line = rawLine.trim();
|
|
127
|
+
if (!line) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const kind = extractSerializedEventKind(line);
|
|
132
|
+
if (!kind || !PERSISTED_EVENT_KINDS.has(kind)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const event = JSON.parse(line);
|
|
138
|
+
if (isConversationEvent(event) && PERSISTED_EVENT_KINDS.has(event.kind)) {
|
|
139
|
+
events.push(event);
|
|
104
140
|
}
|
|
105
|
-
}
|
|
106
|
-
|
|
141
|
+
} catch {
|
|
142
|
+
// Skip malformed lines and oversized legacy transcript events.
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return events;
|
|
107
147
|
} catch (error) {
|
|
108
148
|
if (error?.code === 'ENOENT') {
|
|
109
149
|
return [];
|
|
@@ -14,12 +14,26 @@ import {
|
|
|
14
14
|
findAcpSessionRecord,
|
|
15
15
|
listAcpSessions
|
|
16
16
|
} from '../acp-runtime/session-store.js';
|
|
17
|
+
import { mergeSessionLists } from './sessionListMerge.js';
|
|
17
18
|
|
|
18
19
|
async function flattenLegacyMessages(result) {
|
|
19
20
|
if (Array.isArray(result)) return result;
|
|
20
21
|
return Array.isArray(result?.messages) ? result.messages : [];
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function hasReplayableTranscriptContent(events = []) {
|
|
25
|
+
return (Array.isArray(events) ? events : []).some((event) => (
|
|
26
|
+
event?.kind === 'user_message' ||
|
|
27
|
+
event?.kind === 'assistant_text_delta' ||
|
|
28
|
+
event?.kind === 'assistant_content_block' ||
|
|
29
|
+
event?.kind === 'reasoning_delta' ||
|
|
30
|
+
event?.kind === 'tool_call_start' ||
|
|
31
|
+
event?.kind === 'tool_result' ||
|
|
32
|
+
event?.kind === 'plan_update' ||
|
|
33
|
+
event?.kind === 'system_notice'
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
async function normalizeLegacyLoadResult(result, provider, sessionId) {
|
|
24
38
|
const messages = await flattenLegacyMessages(result);
|
|
25
39
|
const legacyEvents = normalizeLegacyHistoryEntries(messages, provider, sessionId);
|
|
@@ -37,43 +51,46 @@ async function normalizeLegacyLoadResult(result, provider, sessionId) {
|
|
|
37
51
|
};
|
|
38
52
|
}
|
|
39
53
|
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (session?.id) {
|
|
51
|
-
merged.set(session.id, session);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
return Array.from(merged.values()).sort((left, right) => {
|
|
56
|
-
const leftTime = new Date(left?.lastActivity || left?.updatedAt || left?.createdAt || 0).getTime();
|
|
57
|
-
const rightTime = new Date(right?.lastActivity || right?.updatedAt || right?.createdAt || 0).getTime();
|
|
58
|
-
return rightTime - leftTime;
|
|
59
|
-
});
|
|
54
|
+
function createEventLoadResult(events = [], source = 'acp') {
|
|
55
|
+
const normalizedEvents = Array.isArray(events) ? events : [];
|
|
56
|
+
return {
|
|
57
|
+
events: normalizedEvents,
|
|
58
|
+
total: normalizedEvents.length,
|
|
59
|
+
hasMore: false,
|
|
60
|
+
offset: 0,
|
|
61
|
+
limit: null,
|
|
62
|
+
source
|
|
63
|
+
};
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
async function loadAcpEvents(provider, sessionId) {
|
|
66
|
+
async function loadAcpEvents(provider, sessionId, nativeHistoryLoader = null) {
|
|
63
67
|
const record = await findAcpSessionRecord(sessionId, provider);
|
|
64
68
|
if (!record) {
|
|
65
69
|
return null;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
const mirroredEvents = await readMirroredConversationEvents(provider, sessionId);
|
|
73
|
+
if (hasReplayableTranscriptContent(mirroredEvents)) {
|
|
74
|
+
return createEventLoadResult(mirroredEvents, 'acp');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (typeof nativeHistoryLoader === 'function') {
|
|
78
|
+
try {
|
|
79
|
+
const nativeResult = await nativeHistoryLoader();
|
|
80
|
+
const normalizedResult = await normalizeLegacyLoadResult(nativeResult, provider, sessionId);
|
|
81
|
+
if (Array.isArray(normalizedResult)) {
|
|
82
|
+
return createEventLoadResult(normalizedResult, 'acp');
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
...normalizedResult,
|
|
86
|
+
source: 'acp'
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
// Fall back to the mirrored event store when native history is unavailable.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return createEventLoadResult(mirroredEvents, 'acp');
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
const PROVIDER_ADAPTERS = {
|
|
@@ -85,11 +102,16 @@ const PROVIDER_ADAPTERS = {
|
|
|
85
102
|
]);
|
|
86
103
|
return mergeSessionLists(
|
|
87
104
|
(result?.sessions || []).map((session) => ({ ...session, provider: 'claude', source: 'legacy' })),
|
|
88
|
-
acpSessions
|
|
105
|
+
acpSessions,
|
|
106
|
+
{ fallbackProvider: 'claude' }
|
|
89
107
|
);
|
|
90
108
|
},
|
|
91
109
|
async loadEvents({ projectName, sessionId, limit = null, offset = 0 }) {
|
|
92
|
-
const acpResult = await loadAcpEvents(
|
|
110
|
+
const acpResult = await loadAcpEvents(
|
|
111
|
+
'claude',
|
|
112
|
+
sessionId,
|
|
113
|
+
() => getSessionMessages(projectName, sessionId, limit, offset),
|
|
114
|
+
);
|
|
93
115
|
if (acpResult) {
|
|
94
116
|
return acpResult;
|
|
95
117
|
}
|
|
@@ -106,11 +128,16 @@ const PROVIDER_ADAPTERS = {
|
|
|
106
128
|
]);
|
|
107
129
|
return mergeSessionLists(
|
|
108
130
|
sessions.map((session) => ({ ...session, provider: 'codex', source: 'legacy' })),
|
|
109
|
-
acpSessions
|
|
131
|
+
acpSessions,
|
|
132
|
+
{ fallbackProvider: 'codex' }
|
|
110
133
|
);
|
|
111
134
|
},
|
|
112
135
|
async loadEvents({ sessionId, limit = null, offset = 0 }) {
|
|
113
|
-
const acpResult = await loadAcpEvents(
|
|
136
|
+
const acpResult = await loadAcpEvents(
|
|
137
|
+
'codex',
|
|
138
|
+
sessionId,
|
|
139
|
+
() => getCodexSessionMessages(sessionId, limit, offset),
|
|
140
|
+
);
|
|
114
141
|
if (acpResult) {
|
|
115
142
|
return acpResult;
|
|
116
143
|
}
|
|
@@ -127,11 +154,16 @@ const PROVIDER_ADAPTERS = {
|
|
|
127
154
|
]);
|
|
128
155
|
return mergeSessionLists(
|
|
129
156
|
sessions.map((session) => ({ ...session, provider: 'gemini', source: 'legacy' })),
|
|
130
|
-
acpSessions
|
|
157
|
+
acpSessions,
|
|
158
|
+
{ fallbackProvider: 'gemini' }
|
|
131
159
|
);
|
|
132
160
|
},
|
|
133
161
|
async loadEvents({ sessionId, limit = null, offset = 0 }) {
|
|
134
|
-
const acpResult = await loadAcpEvents(
|
|
162
|
+
const acpResult = await loadAcpEvents(
|
|
163
|
+
'gemini',
|
|
164
|
+
sessionId,
|
|
165
|
+
() => getGeminiSessionMessages(sessionId, limit, offset),
|
|
166
|
+
);
|
|
135
167
|
if (acpResult) {
|
|
136
168
|
return acpResult;
|
|
137
169
|
}
|
|
@@ -148,11 +180,16 @@ const PROVIDER_ADAPTERS = {
|
|
|
148
180
|
]);
|
|
149
181
|
return mergeSessionLists(
|
|
150
182
|
sessions.map((session) => ({ ...session, provider: 'opencode', source: 'legacy' })),
|
|
151
|
-
acpSessions
|
|
183
|
+
acpSessions,
|
|
184
|
+
{ fallbackProvider: 'opencode' }
|
|
152
185
|
);
|
|
153
186
|
},
|
|
154
187
|
async loadEvents({ sessionId, limit = null, offset = 0 }) {
|
|
155
|
-
const acpResult = await loadAcpEvents(
|
|
188
|
+
const acpResult = await loadAcpEvents(
|
|
189
|
+
'opencode',
|
|
190
|
+
sessionId,
|
|
191
|
+
() => getOpencodeSessionMessages(sessionId, limit, offset),
|
|
192
|
+
);
|
|
156
193
|
if (acpResult) {
|
|
157
194
|
return acpResult;
|
|
158
195
|
}
|
|
@@ -266,6 +266,14 @@ function createRuntimeSignature(snapshot) {
|
|
|
266
266
|
});
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
export function seedSessionRuntimeSubscriptionSnapshot(subscriptionKey, snapshot) {
|
|
270
|
+
if (!subscriptionKey || !snapshot) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
lastBroadcastSignatures.set(subscriptionKey, createRuntimeSignature(snapshot));
|
|
275
|
+
}
|
|
276
|
+
|
|
269
277
|
export function createSessionRuntimeSubscriptionKey(provider, sessionId) {
|
|
270
278
|
const normalizedProvider = normalizeProvider(provider);
|
|
271
279
|
const normalizedSessionId = normalizeNonEmptyString(sessionId);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function getSessionActivityTime(session) {
|
|
2
|
+
return new Date(
|
|
3
|
+
session?.lastActivity
|
|
4
|
+
|| session?.updatedAt
|
|
5
|
+
|| session?.updated_at
|
|
6
|
+
|| session?.createdAt
|
|
7
|
+
|| session?.created_at
|
|
8
|
+
|| 0
|
|
9
|
+
).getTime();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function buildSessionMergeKey(session, fallbackProvider = '') {
|
|
13
|
+
const provider = String(session?.provider || session?.__provider || fallbackProvider || '').trim().toLowerCase();
|
|
14
|
+
const sessionId = String(session?.id || session?.sessionId || '').trim();
|
|
15
|
+
|
|
16
|
+
if (!sessionId) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return `${provider}:${sessionId}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function mergeSessionLists(legacySessions = [], acpSessions = [], options = {}) {
|
|
24
|
+
const { fallbackProvider = '' } = options;
|
|
25
|
+
const merged = new Map();
|
|
26
|
+
|
|
27
|
+
for (const session of legacySessions) {
|
|
28
|
+
const mergeKey = buildSessionMergeKey(session, fallbackProvider);
|
|
29
|
+
if (!mergeKey) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
merged.set(mergeKey, { ...session });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const session of acpSessions) {
|
|
37
|
+
const mergeKey = buildSessionMergeKey(session, fallbackProvider);
|
|
38
|
+
if (!mergeKey) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const existing = merged.get(mergeKey) || {};
|
|
43
|
+
merged.set(mergeKey, { ...existing, ...session });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Array.from(merged.values()).sort((left, right) => getSessionActivityTime(right) - getSessionActivityTime(left));
|
|
47
|
+
}
|
|
@@ -27,6 +27,9 @@ export const CONVERSATION_EVENT_KINDS = {
|
|
|
27
27
|
PLAN_UPDATE: 'plan_update',
|
|
28
28
|
MODE_UPDATE: 'mode_update',
|
|
29
29
|
AVAILABLE_COMMANDS_UPDATE: 'available_commands_update',
|
|
30
|
+
CONFIG_OPTION_UPDATE: 'config_option_update',
|
|
31
|
+
SESSION_INFO_UPDATE: 'session_info_update',
|
|
32
|
+
USAGE_UPDATE: 'usage_update',
|
|
30
33
|
APPROVAL_REQUEST: 'approval_request',
|
|
31
34
|
APPROVAL_RESOLVED: 'approval_resolved',
|
|
32
35
|
ARTIFACT_CREATED: 'artifact_created',
|
|
@@ -185,11 +188,61 @@ function createTextEndEvent({
|
|
|
185
188
|
];
|
|
186
189
|
}
|
|
187
190
|
|
|
191
|
+
function extractProtocolUserMessage(value) {
|
|
192
|
+
const text = String(value || '').trim();
|
|
193
|
+
if (!text) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const dynamicContextMatch = text.match(
|
|
198
|
+
/<dynamic_context(?:\s[^>]*)?>[\s\S]*?<\/dynamic_context>\s*<user_message(?:\s[^>]*)?>\s*([\s\S]*?)\s*<\/user_message>/i
|
|
199
|
+
);
|
|
200
|
+
if (!dynamicContextMatch) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return String(dynamicContextMatch[1] || '').trim();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function hasAcpProtocolTags(value) {
|
|
208
|
+
return /<\/?(?:subagent_notification|environment_context|dynamic_context|user_message)(?:\s[^>]*)?>/i.test(String(value || ''));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function stripAssistantProtocolTags(text) {
|
|
212
|
+
if (typeof text !== 'string') {
|
|
213
|
+
return text;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const visibleUserMessage = extractProtocolUserMessage(text);
|
|
217
|
+
if (visibleUserMessage != null) {
|
|
218
|
+
return visibleUserMessage;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const hadProtocolTags = hasAcpProtocolTags(text);
|
|
222
|
+
let result = text;
|
|
223
|
+
|
|
224
|
+
result = result.replace(/<subagent_notification(?:\s[^>]*)?>[\s\S]*?<\/subagent_notification>/gi, '');
|
|
225
|
+
result = result.replace(/<environment_context(?:\s[^>]*)?>[\s\S]*?<\/environment_context>/gi, '');
|
|
226
|
+
result = result.replace(/<dynamic_context(?:\s[^>]*)?>[\s\S]*?<\/dynamic_context>/gi, '');
|
|
227
|
+
result = result.replace(/<\/?(?:subagent_notification|environment_context|dynamic_context|user_message)(?:\s[^>]*)?>/gi, '');
|
|
228
|
+
|
|
229
|
+
if (hadProtocolTags) {
|
|
230
|
+
result = result.replace(/["']?\s*\}\s*\}\s*$/g, '');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return result.trim();
|
|
234
|
+
}
|
|
235
|
+
|
|
188
236
|
function extractVisibleUserMessage(value) {
|
|
189
237
|
const text = String(value || '').trim();
|
|
190
238
|
if (!text) return '';
|
|
191
239
|
|
|
192
|
-
if (
|
|
240
|
+
if (
|
|
241
|
+
text.startsWith('# AGENTS.md instructions for ') ||
|
|
242
|
+
text.includes('<environment_context>') ||
|
|
243
|
+
text.startsWith('<subagent_notification>') ||
|
|
244
|
+
text.startsWith('</subagent_notification>')
|
|
245
|
+
) {
|
|
193
246
|
return '';
|
|
194
247
|
}
|
|
195
248
|
|
|
@@ -202,11 +255,9 @@ function extractVisibleUserMessage(value) {
|
|
|
202
255
|
return '';
|
|
203
256
|
}
|
|
204
257
|
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (dynamicContextMatch) {
|
|
209
|
-
return String(dynamicContextMatch[1] || '').trim();
|
|
258
|
+
const protocolUserMessage = extractProtocolUserMessage(text);
|
|
259
|
+
if (protocolUserMessage != null) {
|
|
260
|
+
return protocolUserMessage;
|
|
210
261
|
}
|
|
211
262
|
|
|
212
263
|
return text;
|
|
@@ -222,12 +273,16 @@ function isHiddenSystemContextContent(value) {
|
|
|
222
273
|
text.startsWith('[Dynamic Context]') ||
|
|
223
274
|
text.startsWith('[Hidden Context]') ||
|
|
224
275
|
text.startsWith('# AGENTS.md instructions for ') ||
|
|
225
|
-
text.startsWith('[DYNAMIC CONTEXT V1]')
|
|
276
|
+
text.startsWith('[DYNAMIC CONTEXT V1]') ||
|
|
277
|
+
text.startsWith('<subagent_notification>') ||
|
|
278
|
+
text.startsWith('</subagent_notification>')
|
|
226
279
|
) {
|
|
227
280
|
return true;
|
|
228
281
|
}
|
|
229
282
|
|
|
230
|
-
return /^<dynamic_context(?:\s|>)/i.test(text)
|
|
283
|
+
return /^<dynamic_context(?:\s|>)/i.test(text)
|
|
284
|
+
|| text.includes('<environment_context>')
|
|
285
|
+
|| /<subagent_notification(?:\s|>)/i.test(text);
|
|
231
286
|
}
|
|
232
287
|
|
|
233
288
|
function stringifyToolContent(value) {
|
|
@@ -365,6 +420,71 @@ function normalizeAcpAvailableCommands(value) {
|
|
|
365
420
|
}));
|
|
366
421
|
}
|
|
367
422
|
|
|
423
|
+
function normalizeAcpConfigOptions(value) {
|
|
424
|
+
const configOptions = Array.isArray(value?.configOptions)
|
|
425
|
+
? value.configOptions
|
|
426
|
+
: Array.isArray(value)
|
|
427
|
+
? value
|
|
428
|
+
: [];
|
|
429
|
+
|
|
430
|
+
return configOptions
|
|
431
|
+
.filter((option) => option && typeof option === 'object' && String(option.key || '').trim())
|
|
432
|
+
.map((option) => ({
|
|
433
|
+
key: String(option.key).trim(),
|
|
434
|
+
description: typeof option.description === 'string' ? option.description : '',
|
|
435
|
+
value: cloneJsonValue(option.value),
|
|
436
|
+
schema: option.schema && typeof option.schema === 'object' ? cloneJsonValue(option.schema) : null
|
|
437
|
+
}));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function normalizeAcpSessionInfo(value) {
|
|
441
|
+
if (!value || typeof value !== 'object') {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const title = Object.prototype.hasOwnProperty.call(value, 'title')
|
|
446
|
+
? (value.title == null ? null : String(value.title))
|
|
447
|
+
: undefined;
|
|
448
|
+
const updatedAt = Object.prototype.hasOwnProperty.call(value, 'updatedAt')
|
|
449
|
+
? (value.updatedAt == null ? null : String(value.updatedAt))
|
|
450
|
+
: undefined;
|
|
451
|
+
|
|
452
|
+
if (title === undefined && updatedAt === undefined) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
...(title !== undefined ? { title } : {}),
|
|
458
|
+
...(updatedAt !== undefined ? { updatedAt } : {})
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function normalizeAcpTokenUsage(value) {
|
|
463
|
+
if (!value || typeof value !== 'object') {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const used = Number(value.used);
|
|
468
|
+
const total = Number(value.size);
|
|
469
|
+
if (!Number.isFinite(used) || used < 0 || !Number.isFinite(total) || total < 0) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const safeUsed = Math.round(used);
|
|
474
|
+
const safeTotal = Math.round(total);
|
|
475
|
+
const percentage = safeTotal > 0
|
|
476
|
+
? Math.min(100, Math.max(0, Math.round((safeUsed / safeTotal) * 100)))
|
|
477
|
+
: 0;
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
used: safeUsed,
|
|
481
|
+
total: safeTotal,
|
|
482
|
+
percentage,
|
|
483
|
+
remaining: Math.max(0, safeTotal - safeUsed),
|
|
484
|
+
cost: value.cost && typeof value.cost === 'object' ? cloneJsonValue(value.cost) : null
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
368
488
|
function mergeToolCallSnapshot(existingSnapshot = null, payload = {}) {
|
|
369
489
|
const nextSnapshot = existingSnapshot && typeof existingSnapshot === 'object'
|
|
370
490
|
? { ...existingSnapshot }
|
|
@@ -613,8 +733,11 @@ export function normalizeLegacyHistoryEntries(rawMessages = [], provider = 'clau
|
|
|
613
733
|
if (msg?.message?.role === 'assistant' && msg?.message?.content) {
|
|
614
734
|
if (Array.isArray(msg.message.content)) {
|
|
615
735
|
for (const part of msg.message.content) {
|
|
616
|
-
if (part?.type === 'text'
|
|
617
|
-
|
|
736
|
+
if (part?.type === 'text') {
|
|
737
|
+
const cleanText = stripAssistantProtocolTags(part.text);
|
|
738
|
+
if (cleanText && !isHiddenSystemContextContent(cleanText)) {
|
|
739
|
+
events.push(...createTextSpanEvents({ text: cleanText, provider, sessionId, timestamp, rawRef }));
|
|
740
|
+
}
|
|
618
741
|
} else if (part?.type === 'tool_use') {
|
|
619
742
|
const toolCallId = part.id || createEventId('tool');
|
|
620
743
|
const result = toolResults.get(part.id);
|
|
@@ -633,8 +756,11 @@ export function normalizeLegacyHistoryEntries(rawMessages = [], provider = 'clau
|
|
|
633
756
|
}
|
|
634
757
|
}
|
|
635
758
|
}
|
|
636
|
-
} else
|
|
637
|
-
|
|
759
|
+
} else {
|
|
760
|
+
const cleanText = stripAssistantProtocolTags(msg.message.content);
|
|
761
|
+
if (cleanText && !isHiddenSystemContextContent(cleanText)) {
|
|
762
|
+
events.push(...createTextSpanEvents({ text: cleanText, provider, sessionId, timestamp, rawRef }));
|
|
763
|
+
}
|
|
638
764
|
}
|
|
639
765
|
continue;
|
|
640
766
|
}
|
|
@@ -660,6 +786,12 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
|
|
|
660
786
|
const clientRequestId = getConversationEventClientRequestId(event);
|
|
661
787
|
const optimisticIndex = findMatchingOptimisticUserMessageIndex(nextMessages, event, payload);
|
|
662
788
|
const normalizedContentBlocks = normalizeMessageContentBlocks(payload.contentBlocks);
|
|
789
|
+
const rawText = typeof payload.text === 'string' ? payload.text : '';
|
|
790
|
+
const visibleText = rawText ? extractVisibleUserMessage(rawText) : '';
|
|
791
|
+
|
|
792
|
+
if (rawText.trim() && !visibleText && normalizedContentBlocks.length === 0) {
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
663
795
|
|
|
664
796
|
if (optimisticIndex >= 0) {
|
|
665
797
|
const existingMessage = nextMessages[optimisticIndex] || {};
|
|
@@ -670,7 +802,7 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
|
|
|
670
802
|
nextMessages[optimisticIndex] = {
|
|
671
803
|
...existingMessage,
|
|
672
804
|
type: 'user',
|
|
673
|
-
content:
|
|
805
|
+
content: visibleText || existingMessage.content || '',
|
|
674
806
|
timestamp: existingMessage.timestamp || timestamp,
|
|
675
807
|
provider,
|
|
676
808
|
contentBlocks: nextContentBlocks,
|
|
@@ -683,7 +815,7 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
|
|
|
683
815
|
|
|
684
816
|
nextMessages.push({
|
|
685
817
|
type: 'user',
|
|
686
|
-
content:
|
|
818
|
+
content: visibleText,
|
|
687
819
|
timestamp,
|
|
688
820
|
provider,
|
|
689
821
|
contentBlocks: normalizedContentBlocks,
|
|
@@ -929,6 +1061,9 @@ export function applyConversationEventToTimelineMessages(messages = [], event, s
|
|
|
929
1061
|
case CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED:
|
|
930
1062
|
case CONVERSATION_EVENT_KINDS.MODE_UPDATE:
|
|
931
1063
|
case CONVERSATION_EVENT_KINDS.AVAILABLE_COMMANDS_UPDATE:
|
|
1064
|
+
case CONVERSATION_EVENT_KINDS.CONFIG_OPTION_UPDATE:
|
|
1065
|
+
case CONVERSATION_EVENT_KINDS.SESSION_INFO_UPDATE:
|
|
1066
|
+
case CONVERSATION_EVENT_KINDS.USAGE_UPDATE:
|
|
932
1067
|
case CONVERSATION_EVENT_KINDS.ARTIFACT_CREATED:
|
|
933
1068
|
default:
|
|
934
1069
|
break;
|
|
@@ -952,7 +1087,10 @@ export function conversationEventsToTimelineMessages(events = [], sessionProvide
|
|
|
952
1087
|
export function extractAcpSessionMetadataFromConversationEvents(events = []) {
|
|
953
1088
|
const metadata = {
|
|
954
1089
|
modeState: null,
|
|
955
|
-
availableCommands: []
|
|
1090
|
+
availableCommands: [],
|
|
1091
|
+
configOptions: [],
|
|
1092
|
+
sessionInfo: null,
|
|
1093
|
+
tokenUsage: null
|
|
956
1094
|
};
|
|
957
1095
|
|
|
958
1096
|
for (const event of Array.isArray(events) ? events : []) {
|
|
@@ -977,6 +1115,27 @@ export function extractAcpSessionMetadataFromConversationEvents(events = []) {
|
|
|
977
1115
|
if (event.kind === CONVERSATION_EVENT_KINDS.AVAILABLE_COMMANDS_UPDATE) {
|
|
978
1116
|
metadata.availableCommands = normalizeAcpAvailableCommands(event.payload || {});
|
|
979
1117
|
}
|
|
1118
|
+
|
|
1119
|
+
if (event.kind === CONVERSATION_EVENT_KINDS.CONFIG_OPTION_UPDATE) {
|
|
1120
|
+
metadata.configOptions = normalizeAcpConfigOptions(event.payload || {});
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if (event.kind === CONVERSATION_EVENT_KINDS.SESSION_INFO_UPDATE) {
|
|
1124
|
+
const nextSessionInfo = normalizeAcpSessionInfo(event.payload || {});
|
|
1125
|
+
if (nextSessionInfo) {
|
|
1126
|
+
metadata.sessionInfo = {
|
|
1127
|
+
...(metadata.sessionInfo && typeof metadata.sessionInfo === 'object' ? metadata.sessionInfo : {}),
|
|
1128
|
+
...nextSessionInfo
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (event.kind === CONVERSATION_EVENT_KINDS.USAGE_UPDATE) {
|
|
1134
|
+
const nextTokenUsage = normalizeAcpTokenUsage(event.payload || {});
|
|
1135
|
+
if (nextTokenUsage) {
|
|
1136
|
+
metadata.tokenUsage = nextTokenUsage;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
980
1139
|
}
|
|
981
1140
|
|
|
982
1141
|
return metadata;
|