codemini-cli 0.6.3 → 0.6.4
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/codemini-web/dist/assets/{AboutDialog-jgqGjQgl.js → AboutDialog-MRopwNIL.js} +2 -2
- package/codemini-web/dist/assets/CodeWikiPanel-UpK5xGE3.js +1 -0
- package/codemini-web/dist/assets/ConfigDialog-CNl28wsj.js +1 -0
- package/codemini-web/dist/assets/GitDiffDialog-gSysUg2J.js +3 -0
- package/codemini-web/dist/assets/{MemoryDialog-BhxQgG0I.js → MemoryDialog-DFUmo3Kl.js} +3 -3
- package/codemini-web/dist/assets/MessageBubble-CGnnViv0.js +12 -0
- package/codemini-web/dist/assets/PatchDiff-B8rwvEg5.js +230 -0
- package/codemini-web/dist/assets/ProjectSelector-BF59M1zb.js +1 -0
- package/codemini-web/dist/assets/{SkillDialog-DxS43NpR.js → SkillDialog-CQTjbSiw.js} +4 -4
- package/codemini-web/dist/assets/SoulDialog-BLjUGqqB.js +1 -0
- package/codemini-web/dist/assets/chevron-right--85xg7qk.js +1 -0
- package/codemini-web/dist/assets/{chunk-BO2N2NFS-Budy_hfO.js → chunk-BO2N2NFS-6uELoidu.js} +6 -6
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-CQS1PAvD.js → highlighted-body-OFNGDK62-gb1UMBZ5.js} +1 -1
- package/codemini-web/dist/assets/index-1xqD0R5t.css +2 -0
- package/codemini-web/dist/assets/index-CDXQGwPs.js +65 -0
- package/codemini-web/dist/assets/{input-CNQgbKe6.js → input-Ca8O_061.js} +1 -1
- package/codemini-web/dist/assets/lib-BXWizt13.js +1 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-ROliF8Yd.js +1 -0
- package/codemini-web/dist/assets/{pencil-Ce_LFiEh.js → pencil-BhT11Ztp.js} +1 -1
- package/codemini-web/dist/assets/{refresh-cw-BKL-AZu5.js → refresh-cw-D7R5Lth6.js} +1 -1
- package/codemini-web/dist/assets/select-DBvcHBzs.js +1 -0
- package/codemini-web/dist/assets/{trash-2-KmAlCwXd.js → trash-2-BfNZcWfX.js} +1 -1
- package/codemini-web/dist/index.html +2 -2
- package/codemini-web/lib/runtime-bridge.js +325 -296
- package/codemini-web/server.js +310 -243
- package/package.json +1 -1
- package/src/core/agent-loop.js +188 -97
- package/src/core/chat-runtime.js +674 -571
- package/src/core/config-store.js +11 -3
- package/src/core/git-oplog-change-tracker.js +387 -0
- package/src/core/non-git-backup.js +116 -0
- package/src/core/paths.js +123 -123
- package/src/core/session-store.js +148 -99
- package/src/core/tools.js +499 -456
- package/src/tui/chat-app.js +196 -56
- package/codemini-web/dist/assets/CodeWikiPanel-EPuoerNv.js +0 -1
- package/codemini-web/dist/assets/ConfigDialog-B5IGZCc9.js +0 -1
- package/codemini-web/dist/assets/GitDiffDialog-Bb_Tw5ZK.js +0 -222
- package/codemini-web/dist/assets/MessageBubble-wUff4GP4.js +0 -6
- package/codemini-web/dist/assets/ProjectSelector-C0leTf6f.js +0 -1
- package/codemini-web/dist/assets/SoulDialog-XDTEGWvH.js +0 -1
- package/codemini-web/dist/assets/chevron-right-Dbzw7YzA.js +0 -1
- package/codemini-web/dist/assets/index-D0EGtNPr.js +0 -65
- package/codemini-web/dist/assets/index-wOUf3WkN.css +0 -2
- package/codemini-web/dist/assets/lib-BOngVP_M.js +0 -11
- package/codemini-web/dist/assets/lib-DrOTTm_N.js +0 -1
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-DrBu5KyC.js +0 -1
- package/codemini-web/dist/assets/select-BZXfigic.js +0 -1
|
@@ -47,177 +47,177 @@ function updateToolInSegments(segments, toolId, updater) {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function appendTextSegment(segments, delta, isStreaming = true) {
|
|
51
|
-
const value = String(delta || '');
|
|
52
|
-
if (!value) return segments || [];
|
|
53
|
-
const current = Array.isArray(segments) ? segments : [];
|
|
50
|
+
function appendTextSegment(segments, delta, isStreaming = true) {
|
|
51
|
+
const value = String(delta || '');
|
|
52
|
+
if (!value) return segments || [];
|
|
53
|
+
const current = Array.isArray(segments) ? segments : [];
|
|
54
54
|
const last = current[current.length - 1];
|
|
55
55
|
if (last?.type === 'text') {
|
|
56
56
|
return [
|
|
57
57
|
...current.slice(0, -1),
|
|
58
58
|
{ ...last, text: `${last.text || ''}${value}`, isStreaming }
|
|
59
59
|
];
|
|
60
|
-
}
|
|
61
|
-
return [...current, { type: 'text', text: value, isStreaming }];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function replaceTextSegment(segments, text, isStreaming = false) {
|
|
65
|
-
const value = String(text || '');
|
|
66
|
-
const current = Array.isArray(segments) ? segments : [];
|
|
67
|
-
const index = current.findLastIndex((seg) => seg?.type === 'text');
|
|
68
|
-
if (index === -1) return value ? [...current, { type: 'text', text: value, isStreaming }] : current;
|
|
69
|
-
return current.map((seg, i) => (
|
|
70
|
-
i === index ? { ...seg, text: value, isStreaming } : seg
|
|
71
|
-
));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function appendThinkingSegment(segments, delta, isStreaming = true) {
|
|
75
|
-
const value = String(delta || '');
|
|
76
|
-
if (!value) return segments || [];
|
|
77
|
-
const current = Array.isArray(segments) ? segments : [];
|
|
78
|
-
const now = new Date().toISOString();
|
|
79
|
-
const nowMs = Date.parse(now);
|
|
80
|
-
const last = current[current.length - 1];
|
|
81
|
-
if (last?.type === 'thinking') {
|
|
82
|
-
const startedAt = last.startedAt || now;
|
|
83
|
-
return [
|
|
84
|
-
...current.slice(0, -1),
|
|
85
|
-
{
|
|
86
|
-
...last,
|
|
87
|
-
text: `${last.text || ''}${value}`,
|
|
88
|
-
isStreaming,
|
|
89
|
-
startedAt,
|
|
90
|
-
endedAt: isStreaming ? null : (last.endedAt || now),
|
|
91
|
-
durationMs: Math.max(Number(last.durationMs || 0), nowMs - Date.parse(startedAt))
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
}
|
|
95
|
-
return [...current, { type: 'thinking', text: value, isStreaming, startedAt: now, endedAt: isStreaming ? null : now, durationMs: isStreaming ? 0 : null }];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function resolveThinkingDurationMs(seg, endedAt) {
|
|
99
|
-
const explicit = Number(seg?.durationMs);
|
|
100
|
-
const startMs = Date.parse(seg?.startedAt || '');
|
|
101
|
-
const endMs = Date.parse(seg?.endedAt || endedAt || '');
|
|
102
|
-
const measured = Number.isFinite(startMs) && Number.isFinite(endMs) ? Math.max(0, endMs - startMs) : null;
|
|
103
|
-
if (Number.isFinite(explicit) && measured != null) return Math.max(explicit, measured);
|
|
104
|
-
if (Number.isFinite(explicit)) return Math.max(0, explicit);
|
|
105
|
-
return measured;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function finishThinkingSegments(segments) {
|
|
109
|
-
const endedAt = new Date().toISOString();
|
|
110
|
-
return (Array.isArray(segments) ? segments : []).map((seg) => (
|
|
111
|
-
seg.type === 'thinking'
|
|
112
|
-
? {
|
|
113
|
-
...seg,
|
|
114
|
-
isStreaming: false,
|
|
115
|
-
endedAt: seg.endedAt || endedAt,
|
|
116
|
-
durationMs: resolveThinkingDurationMs(seg, endedAt)
|
|
117
|
-
}
|
|
118
|
-
: seg
|
|
119
|
-
));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getReasoningTextFromAssistantMessage(message = {}) {
|
|
123
|
-
if (typeof message.reasoning_content === 'string' && message.reasoning_content.trim()) {
|
|
124
|
-
return message.reasoning_content.trim();
|
|
125
|
-
}
|
|
126
|
-
if (!Array.isArray(message.reasoning_details)) return '';
|
|
127
|
-
return message.reasoning_details
|
|
128
|
-
.map((block) => {
|
|
129
|
-
if (!block || typeof block !== 'object') return '';
|
|
130
|
-
if (block.type === 'thinking') return block.thinking || block.text || '';
|
|
131
|
-
if (block.type === 'reasoning' || block.type === 'reasoning_content') return block.text || block.reasoning_content || '';
|
|
132
|
-
if (block.type === 'redacted_thinking') return '[redacted thinking]';
|
|
133
|
-
return '';
|
|
134
|
-
})
|
|
135
|
-
.filter(Boolean)
|
|
136
|
-
.join('\n\n')
|
|
137
|
-
.trim();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function hasThinkingSegment(segments) {
|
|
141
|
-
return (Array.isArray(segments) ? segments : []).some((seg) => seg.type === 'thinking' && String(seg.text || '').trim());
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function normalizeUiUsage(usage) {
|
|
145
|
-
if (!usage || typeof usage !== 'object') return null;
|
|
146
|
-
const out = {};
|
|
147
|
-
for (const key of ['inputTokens', 'outputTokens', 'totalTokens', 'cachedInputTokens', 'cacheMissInputTokens', 'cacheWriteInputTokens', 'reasoningOutputTokens', 'requests']) {
|
|
148
|
-
const value = Number(usage?.[key]);
|
|
149
|
-
if (Number.isFinite(value)) out[key] = Math.max(0, Math.round(value));
|
|
150
|
-
}
|
|
151
|
-
return Object.keys(out).length ? out : null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function mergeUiUsage(left, right) {
|
|
155
|
-
const a = normalizeUiUsage(left);
|
|
156
|
-
const b = normalizeUiUsage(right);
|
|
157
|
-
if (!a) return b;
|
|
158
|
-
if (!b) return a;
|
|
159
|
-
const out = {};
|
|
160
|
-
for (const key of ['inputTokens', 'outputTokens', 'totalTokens', 'cachedInputTokens', 'cacheMissInputTokens', 'cacheWriteInputTokens', 'reasoningOutputTokens', 'requests']) {
|
|
161
|
-
out[key] = Math.max(0, Math.round(Number(a[key] || 0) + Number(b[key] || 0)));
|
|
162
|
-
}
|
|
163
|
-
return out;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function toCodeWikiGenerateProgress(event) {
|
|
167
|
-
if (!event?.type) return null;
|
|
168
|
-
const now = new Date().toISOString();
|
|
169
|
-
if (event.type === 'plan:steps') {
|
|
170
|
-
return {
|
|
171
|
-
type: 'codewiki:generate_progress',
|
|
172
|
-
phase: 'steps',
|
|
173
|
-
timestamp: now,
|
|
174
|
-
steps: (Array.isArray(event.steps) ? event.steps : []).map((step, index) => ({
|
|
175
|
-
index: Number(step.index || index + 1),
|
|
176
|
-
title: step.title || '',
|
|
177
|
-
role: step.role || 'general',
|
|
178
|
-
status: step.status || 'pending'
|
|
179
|
-
}))
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
if (event.type === 'plan:step_start') {
|
|
183
|
-
return {
|
|
184
|
-
type: 'codewiki:generate_progress',
|
|
185
|
-
phase: 'step_start',
|
|
186
|
-
timestamp: now,
|
|
187
|
-
step: Number(event.step || 0),
|
|
188
|
-
total: Number(event.total || 0),
|
|
189
|
-
role: event.role || 'general',
|
|
190
|
-
title: event.title || '',
|
|
191
|
-
status: 'running'
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
if (event.type === 'plan:step_done' || event.type === 'plan:progress') {
|
|
195
|
-
return {
|
|
196
|
-
type: 'codewiki:generate_progress',
|
|
197
|
-
phase: event.type === 'plan:step_done' ? 'step_done' : 'step_progress',
|
|
198
|
-
timestamp: now,
|
|
199
|
-
step: Number(event.step || 0),
|
|
200
|
-
total: Number(event.total || 0),
|
|
201
|
-
role: event.role || 'general',
|
|
202
|
-
title: event.title || '',
|
|
203
|
-
status: event.status || (event.type === 'plan:step_done' ? 'done' : 'running'),
|
|
204
|
-
summary: event.summary || ''
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
if (event.type === 'skill:start' || event.type === 'skill:end' || event.type === 'skill:error') {
|
|
208
|
-
return {
|
|
209
|
-
type: 'codewiki:generate_progress',
|
|
210
|
-
phase: event.type.replace('skill:', 'skill_'),
|
|
211
|
-
timestamp: now,
|
|
212
|
-
name: event.name || 'project-requirements',
|
|
213
|
-
status: event.type === 'skill:error' ? 'failed' : event.type === 'skill:end' ? 'done' : 'running',
|
|
214
|
-
summary: event.summary || ''
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function createPlanStepUiMessage(event) {
|
|
60
|
+
}
|
|
61
|
+
return [...current, { type: 'text', text: value, isStreaming }];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function replaceTextSegment(segments, text, isStreaming = false) {
|
|
65
|
+
const value = String(text || '');
|
|
66
|
+
const current = Array.isArray(segments) ? segments : [];
|
|
67
|
+
const index = current.findLastIndex((seg) => seg?.type === 'text');
|
|
68
|
+
if (index === -1) return value ? [...current, { type: 'text', text: value, isStreaming }] : current;
|
|
69
|
+
return current.map((seg, i) => (
|
|
70
|
+
i === index ? { ...seg, text: value, isStreaming } : seg
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function appendThinkingSegment(segments, delta, isStreaming = true) {
|
|
75
|
+
const value = String(delta || '');
|
|
76
|
+
if (!value) return segments || [];
|
|
77
|
+
const current = Array.isArray(segments) ? segments : [];
|
|
78
|
+
const now = new Date().toISOString();
|
|
79
|
+
const nowMs = Date.parse(now);
|
|
80
|
+
const last = current[current.length - 1];
|
|
81
|
+
if (last?.type === 'thinking') {
|
|
82
|
+
const startedAt = last.startedAt || now;
|
|
83
|
+
return [
|
|
84
|
+
...current.slice(0, -1),
|
|
85
|
+
{
|
|
86
|
+
...last,
|
|
87
|
+
text: `${last.text || ''}${value}`,
|
|
88
|
+
isStreaming,
|
|
89
|
+
startedAt,
|
|
90
|
+
endedAt: isStreaming ? null : (last.endedAt || now),
|
|
91
|
+
durationMs: Math.max(Number(last.durationMs || 0), nowMs - Date.parse(startedAt))
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
return [...current, { type: 'thinking', text: value, isStreaming, startedAt: now, endedAt: isStreaming ? null : now, durationMs: isStreaming ? 0 : null }];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function resolveThinkingDurationMs(seg, endedAt) {
|
|
99
|
+
const explicit = Number(seg?.durationMs);
|
|
100
|
+
const startMs = Date.parse(seg?.startedAt || '');
|
|
101
|
+
const endMs = Date.parse(seg?.endedAt || endedAt || '');
|
|
102
|
+
const measured = Number.isFinite(startMs) && Number.isFinite(endMs) ? Math.max(0, endMs - startMs) : null;
|
|
103
|
+
if (Number.isFinite(explicit) && measured != null) return Math.max(explicit, measured);
|
|
104
|
+
if (Number.isFinite(explicit)) return Math.max(0, explicit);
|
|
105
|
+
return measured;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function finishThinkingSegments(segments) {
|
|
109
|
+
const endedAt = new Date().toISOString();
|
|
110
|
+
return (Array.isArray(segments) ? segments : []).map((seg) => (
|
|
111
|
+
seg.type === 'thinking'
|
|
112
|
+
? {
|
|
113
|
+
...seg,
|
|
114
|
+
isStreaming: false,
|
|
115
|
+
endedAt: seg.endedAt || endedAt,
|
|
116
|
+
durationMs: resolveThinkingDurationMs(seg, endedAt)
|
|
117
|
+
}
|
|
118
|
+
: seg
|
|
119
|
+
));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getReasoningTextFromAssistantMessage(message = {}) {
|
|
123
|
+
if (typeof message.reasoning_content === 'string' && message.reasoning_content.trim()) {
|
|
124
|
+
return message.reasoning_content.trim();
|
|
125
|
+
}
|
|
126
|
+
if (!Array.isArray(message.reasoning_details)) return '';
|
|
127
|
+
return message.reasoning_details
|
|
128
|
+
.map((block) => {
|
|
129
|
+
if (!block || typeof block !== 'object') return '';
|
|
130
|
+
if (block.type === 'thinking') return block.thinking || block.text || '';
|
|
131
|
+
if (block.type === 'reasoning' || block.type === 'reasoning_content') return block.text || block.reasoning_content || '';
|
|
132
|
+
if (block.type === 'redacted_thinking') return '[redacted thinking]';
|
|
133
|
+
return '';
|
|
134
|
+
})
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.join('\n\n')
|
|
137
|
+
.trim();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function hasThinkingSegment(segments) {
|
|
141
|
+
return (Array.isArray(segments) ? segments : []).some((seg) => seg.type === 'thinking' && String(seg.text || '').trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeUiUsage(usage) {
|
|
145
|
+
if (!usage || typeof usage !== 'object') return null;
|
|
146
|
+
const out = {};
|
|
147
|
+
for (const key of ['inputTokens', 'outputTokens', 'totalTokens', 'cachedInputTokens', 'cacheMissInputTokens', 'cacheWriteInputTokens', 'reasoningOutputTokens', 'requests']) {
|
|
148
|
+
const value = Number(usage?.[key]);
|
|
149
|
+
if (Number.isFinite(value)) out[key] = Math.max(0, Math.round(value));
|
|
150
|
+
}
|
|
151
|
+
return Object.keys(out).length ? out : null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function mergeUiUsage(left, right) {
|
|
155
|
+
const a = normalizeUiUsage(left);
|
|
156
|
+
const b = normalizeUiUsage(right);
|
|
157
|
+
if (!a) return b;
|
|
158
|
+
if (!b) return a;
|
|
159
|
+
const out = {};
|
|
160
|
+
for (const key of ['inputTokens', 'outputTokens', 'totalTokens', 'cachedInputTokens', 'cacheMissInputTokens', 'cacheWriteInputTokens', 'reasoningOutputTokens', 'requests']) {
|
|
161
|
+
out[key] = Math.max(0, Math.round(Number(a[key] || 0) + Number(b[key] || 0)));
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function toCodeWikiGenerateProgress(event) {
|
|
167
|
+
if (!event?.type) return null;
|
|
168
|
+
const now = new Date().toISOString();
|
|
169
|
+
if (event.type === 'plan:steps') {
|
|
170
|
+
return {
|
|
171
|
+
type: 'codewiki:generate_progress',
|
|
172
|
+
phase: 'steps',
|
|
173
|
+
timestamp: now,
|
|
174
|
+
steps: (Array.isArray(event.steps) ? event.steps : []).map((step, index) => ({
|
|
175
|
+
index: Number(step.index || index + 1),
|
|
176
|
+
title: step.title || '',
|
|
177
|
+
role: step.role || 'general',
|
|
178
|
+
status: step.status || 'pending'
|
|
179
|
+
}))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (event.type === 'plan:step_start') {
|
|
183
|
+
return {
|
|
184
|
+
type: 'codewiki:generate_progress',
|
|
185
|
+
phase: 'step_start',
|
|
186
|
+
timestamp: now,
|
|
187
|
+
step: Number(event.step || 0),
|
|
188
|
+
total: Number(event.total || 0),
|
|
189
|
+
role: event.role || 'general',
|
|
190
|
+
title: event.title || '',
|
|
191
|
+
status: 'running'
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (event.type === 'plan:step_done' || event.type === 'plan:progress') {
|
|
195
|
+
return {
|
|
196
|
+
type: 'codewiki:generate_progress',
|
|
197
|
+
phase: event.type === 'plan:step_done' ? 'step_done' : 'step_progress',
|
|
198
|
+
timestamp: now,
|
|
199
|
+
step: Number(event.step || 0),
|
|
200
|
+
total: Number(event.total || 0),
|
|
201
|
+
role: event.role || 'general',
|
|
202
|
+
title: event.title || '',
|
|
203
|
+
status: event.status || (event.type === 'plan:step_done' ? 'done' : 'running'),
|
|
204
|
+
summary: event.summary || ''
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (event.type === 'skill:start' || event.type === 'skill:end' || event.type === 'skill:error') {
|
|
208
|
+
return {
|
|
209
|
+
type: 'codewiki:generate_progress',
|
|
210
|
+
phase: event.type.replace('skill:', 'skill_'),
|
|
211
|
+
timestamp: now,
|
|
212
|
+
name: event.name || 'project-requirements',
|
|
213
|
+
status: event.type === 'skill:error' ? 'failed' : event.type === 'skill:end' ? 'done' : 'running',
|
|
214
|
+
summary: event.summary || ''
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function createPlanStepUiMessage(event) {
|
|
221
221
|
return {
|
|
222
222
|
id: `plan-step-${event.step}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
223
223
|
role: event.role || 'general',
|
|
@@ -360,81 +360,86 @@ export class RuntimeBridge {
|
|
|
360
360
|
}
|
|
361
361
|
break;
|
|
362
362
|
}
|
|
363
|
-
case 'assistant:delta': {
|
|
364
|
-
const delta = stripPlanProgressText(event.text);
|
|
365
|
-
if (this.#uiActiveMsgId && delta) {
|
|
366
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
367
|
-
...message,
|
|
368
|
-
segments: appendTextSegment(finishThinkingSegments(message.segments), delta, true)
|
|
369
|
-
}));
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case 'assistant:reasoning_delta': {
|
|
374
|
-
if (this.#uiActiveMsgId && event.text) {
|
|
375
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
376
|
-
...message,
|
|
377
|
-
segments: appendThinkingSegment(message.segments, event.text, true)
|
|
378
|
-
}));
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
case 'assistant:response': {
|
|
383
|
-
const reasoningText = getReasoningTextFromAssistantMessage(event.assistantMessage);
|
|
384
|
-
if (this.#uiActiveMsgId && reasoningText) {
|
|
385
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
386
|
-
...message,
|
|
387
|
-
segments: hasThinkingSegment(message.segments)
|
|
388
|
-
? message.segments
|
|
389
|
-
: appendThinkingSegment(message.segments, reasoningText, false)
|
|
390
|
-
}));
|
|
391
|
-
}
|
|
392
|
-
if (this.#uiActiveMsgId && event.text) {
|
|
393
|
-
const text = stripPlanProgressText(event.text);
|
|
394
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
395
|
-
...message,
|
|
396
|
-
segments: text
|
|
397
|
-
? replaceTextSegment(finishThinkingSegments(message.segments), text, false)
|
|
398
|
-
: message.segments
|
|
399
|
-
}));
|
|
400
|
-
}
|
|
401
|
-
if (this.#uiActiveMsgId) {
|
|
402
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
403
|
-
...message,
|
|
404
|
-
segments: finishThinkingSegments(message.segments),
|
|
405
|
-
usage: mergeUiUsage(message.usage, event.usage || event.assistantMessage?.usage) || message.usage || null
|
|
406
|
-
}));
|
|
407
|
-
}
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
363
|
+
case 'assistant:delta': {
|
|
364
|
+
const delta = stripPlanProgressText(event.text);
|
|
365
|
+
if (this.#uiActiveMsgId && delta) {
|
|
366
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
367
|
+
...message,
|
|
368
|
+
segments: appendTextSegment(finishThinkingSegments(message.segments), delta, true)
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case 'assistant:reasoning_delta': {
|
|
374
|
+
if (this.#uiActiveMsgId && event.text) {
|
|
375
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
376
|
+
...message,
|
|
377
|
+
segments: appendThinkingSegment(message.segments, event.text, true)
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
case 'assistant:response': {
|
|
383
|
+
const reasoningText = getReasoningTextFromAssistantMessage(event.assistantMessage);
|
|
384
|
+
if (this.#uiActiveMsgId && reasoningText) {
|
|
385
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
386
|
+
...message,
|
|
387
|
+
segments: hasThinkingSegment(message.segments)
|
|
388
|
+
? message.segments
|
|
389
|
+
: appendThinkingSegment(message.segments, reasoningText, false)
|
|
390
|
+
}));
|
|
391
|
+
}
|
|
392
|
+
if (this.#uiActiveMsgId && event.text) {
|
|
393
|
+
const text = stripPlanProgressText(event.text);
|
|
394
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
395
|
+
...message,
|
|
396
|
+
segments: text
|
|
397
|
+
? replaceTextSegment(finishThinkingSegments(message.segments), text, false)
|
|
398
|
+
: message.segments
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
if (this.#uiActiveMsgId) {
|
|
402
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
403
|
+
...message,
|
|
404
|
+
segments: finishThinkingSegments(message.segments),
|
|
405
|
+
usage: mergeUiUsage(message.usage, event.usage || event.assistantMessage?.usage) || message.usage || null
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
410
|
case 'tool:start': {
|
|
411
|
-
if (this.#uiActiveMsgId) {
|
|
412
|
-
const toolCard = { id: event.id, name: event.name, arguments: event.arguments, status: 'running', durationMs: null, summary: '', result: '' };
|
|
413
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
414
|
-
...message,
|
|
415
|
-
segments: addToolToSegments(finishThinkingSegments(message.segments), toolCard)
|
|
416
|
-
}));
|
|
417
|
-
}
|
|
411
|
+
if (this.#uiActiveMsgId) {
|
|
412
|
+
const toolCard = { id: event.id, name: event.name, arguments: event.arguments, status: 'running', durationMs: null, summary: '', result: '' };
|
|
413
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
414
|
+
...message,
|
|
415
|
+
segments: addToolToSegments(finishThinkingSegments(message.segments), toolCard)
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
418
|
break;
|
|
419
419
|
}
|
|
420
420
|
case 'tool:end': {
|
|
421
421
|
if (this.#uiActiveMsgId) {
|
|
422
|
+
const eventChanges = Array.isArray(event.fileChanges) && event.fileChanges.length
|
|
423
|
+
? event.fileChanges
|
|
424
|
+
: (event.fileChange?.path ? [event.fileChange] : []);
|
|
422
425
|
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
423
426
|
...message,
|
|
424
|
-
fileChanges:
|
|
425
|
-
? [...(Array.isArray(message.fileChanges) ? message.fileChanges : []),
|
|
427
|
+
fileChanges: eventChanges.length
|
|
428
|
+
? [...(Array.isArray(message.fileChanges) ? message.fileChanges : []), ...eventChanges]
|
|
426
429
|
: (Array.isArray(message.fileChanges) ? message.fileChanges : []),
|
|
427
430
|
segments: updateToolInSegments(message.segments, event.id, (card) => ({
|
|
428
431
|
...card,
|
|
429
432
|
status: 'done',
|
|
430
433
|
durationMs: event.durationMs,
|
|
431
434
|
summary: event.summary || card.summary,
|
|
432
|
-
...(event.
|
|
435
|
+
...(event.resultMeta ? { resultMeta: event.resultMeta } : {}),
|
|
436
|
+
...(event.fileChange ? { fileChange: event.fileChange } : {}),
|
|
437
|
+
...(eventChanges.length ? { fileChanges: eventChanges } : {})
|
|
433
438
|
}))
|
|
434
439
|
}));
|
|
435
440
|
}
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
438
443
|
case 'tool:result': {
|
|
439
444
|
if (this.#uiActiveMsgId) {
|
|
440
445
|
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
@@ -548,7 +553,7 @@ export class RuntimeBridge {
|
|
|
548
553
|
return this.#runtime.consumeStartupEvents();
|
|
549
554
|
}
|
|
550
555
|
|
|
551
|
-
handleSubmit(line, options = {}) {
|
|
556
|
+
handleSubmit(line, options = {}) {
|
|
552
557
|
if (this.#busy) return { error: true, message: 'A request is already in progress' };
|
|
553
558
|
this.#resetUiTranscriptIfSessionChanged();
|
|
554
559
|
const trimmed = String(line || '').trim();
|
|
@@ -566,13 +571,13 @@ export class RuntimeBridge {
|
|
|
566
571
|
this.#recordUiEvent(event);
|
|
567
572
|
this.#broadcast(event);
|
|
568
573
|
}, options).then((result) => {
|
|
569
|
-
if (this.#uiActiveMsgId) {
|
|
570
|
-
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
571
|
-
...message,
|
|
572
|
-
segments: finishThinkingSegments(message.segments)
|
|
573
|
-
.map((seg) => seg.type === 'text' ? { ...seg, isStreaming: false } : seg)
|
|
574
|
-
}));
|
|
575
|
-
}
|
|
574
|
+
if (this.#uiActiveMsgId) {
|
|
575
|
+
this.#updateUiMessage(this.#uiActiveMsgId, (message) => ({
|
|
576
|
+
...message,
|
|
577
|
+
segments: finishThinkingSegments(message.segments)
|
|
578
|
+
.map((seg) => seg.type === 'text' ? { ...seg, isStreaming: false } : seg)
|
|
579
|
+
}));
|
|
580
|
+
}
|
|
576
581
|
this.#uiActiveMsgId = null;
|
|
577
582
|
this.#uiPlanStepIds = new Map();
|
|
578
583
|
this.#broadcast({ type: 'submit:done', result: { type: result.type, aborted: result.aborted, text: result.text } });
|
|
@@ -587,39 +592,39 @@ export class RuntimeBridge {
|
|
|
587
592
|
this.#busy = false;
|
|
588
593
|
this.#broadcastRuntimeState();
|
|
589
594
|
});
|
|
590
|
-
return { accepted: true };
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
handleCodeWikiGenerate(line) {
|
|
594
|
-
if (this.#busy) return { error: true, message: 'A request is already in progress' };
|
|
595
|
-
this.#busy = true;
|
|
596
|
-
this.#broadcastRuntimeState();
|
|
597
|
-
const emitProgress = (event) => {
|
|
598
|
-
const progress = toCodeWikiGenerateProgress(event);
|
|
599
|
-
if (progress) this.#broadcast(progress);
|
|
600
|
-
};
|
|
601
|
-
this.#runtime.submit(line, emitProgress, { codeWikiGenerate: true }).then((result) => {
|
|
602
|
-
this.#broadcast({
|
|
603
|
-
type: 'codewiki:generate_done',
|
|
604
|
-
result: {
|
|
605
|
-
type: result?.type || 'assistant',
|
|
606
|
-
aborted: !!result?.aborted,
|
|
607
|
-
text: result?.text || ''
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
}).catch((err) => {
|
|
611
|
-
this.#broadcast({
|
|
612
|
-
type: 'codewiki:generate_error',
|
|
613
|
-
message: err?.message || 'CodeWiki generation failed'
|
|
614
|
-
});
|
|
615
|
-
}).finally(() => {
|
|
616
|
-
this.#busy = false;
|
|
617
|
-
this.#broadcastRuntimeState();
|
|
618
|
-
});
|
|
619
|
-
return { accepted: true };
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
async handleCodeWikiAsk(line, onEvent = null) {
|
|
595
|
+
return { accepted: true };
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
handleCodeWikiGenerate(line) {
|
|
599
|
+
if (this.#busy) return { error: true, message: 'A request is already in progress' };
|
|
600
|
+
this.#busy = true;
|
|
601
|
+
this.#broadcastRuntimeState();
|
|
602
|
+
const emitProgress = (event) => {
|
|
603
|
+
const progress = toCodeWikiGenerateProgress(event);
|
|
604
|
+
if (progress) this.#broadcast(progress);
|
|
605
|
+
};
|
|
606
|
+
this.#runtime.submit(line, emitProgress, { codeWikiGenerate: true }).then((result) => {
|
|
607
|
+
this.#broadcast({
|
|
608
|
+
type: 'codewiki:generate_done',
|
|
609
|
+
result: {
|
|
610
|
+
type: result?.type || 'assistant',
|
|
611
|
+
aborted: !!result?.aborted,
|
|
612
|
+
text: result?.text || ''
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}).catch((err) => {
|
|
616
|
+
this.#broadcast({
|
|
617
|
+
type: 'codewiki:generate_error',
|
|
618
|
+
message: err?.message || 'CodeWiki generation failed'
|
|
619
|
+
});
|
|
620
|
+
}).finally(() => {
|
|
621
|
+
this.#busy = false;
|
|
622
|
+
this.#broadcastRuntimeState();
|
|
623
|
+
});
|
|
624
|
+
return { accepted: true };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async handleCodeWikiAsk(line, onEvent = null) {
|
|
623
628
|
if (this.#busy) return { error: true, message: 'A request is already in progress' };
|
|
624
629
|
this.#busy = true;
|
|
625
630
|
const emit = (event) => {
|
|
@@ -652,27 +657,34 @@ export class RuntimeBridge {
|
|
|
652
657
|
return this.#runtime.abort();
|
|
653
658
|
}
|
|
654
659
|
|
|
655
|
-
async setExecutionMode(mode) {
|
|
656
|
-
if (this.#busy) return false;
|
|
657
|
-
const ok = await this.#runtime.setExecutionMode(mode);
|
|
658
|
-
if (ok) this.#broadcast({ type: 'mode:changed', mode, ...this.getState() });
|
|
659
|
-
return ok;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
async reloadConfig(options = {}) {
|
|
663
|
-
return this.#runtime.reloadConfig?.(options);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
async reloadCommandsAndSkills() {
|
|
667
|
-
const ok = await this.#runtime.reloadCommandsAndSkills?.();
|
|
668
|
-
if (ok) this.#broadcastRuntimeState();
|
|
660
|
+
async setExecutionMode(mode) {
|
|
661
|
+
if (this.#busy) return false;
|
|
662
|
+
const ok = await this.#runtime.setExecutionMode(mode);
|
|
663
|
+
if (ok) this.#broadcast({ type: 'mode:changed', mode, ...this.getState() });
|
|
669
664
|
return ok;
|
|
670
665
|
}
|
|
671
666
|
|
|
672
|
-
|
|
673
|
-
|
|
667
|
+
async setApprovalMode(mode) {
|
|
668
|
+
if (this.#busy) return false;
|
|
669
|
+
const ok = await this.#runtime.setApprovalMode?.(mode);
|
|
670
|
+
if (ok) this.#broadcast({ type: 'approval-mode:changed', approvalMode: mode, ...this.getState() });
|
|
671
|
+
return ok;
|
|
674
672
|
}
|
|
675
673
|
|
|
674
|
+
async reloadConfig(options = {}) {
|
|
675
|
+
return this.#runtime.reloadConfig?.(options);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
async reloadCommandsAndSkills() {
|
|
679
|
+
const ok = await this.#runtime.reloadCommandsAndSkills?.();
|
|
680
|
+
if (ok) this.#broadcastRuntimeState();
|
|
681
|
+
return ok;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
handleApproval(id, approved) {
|
|
685
|
+
return this.#approval.resolve(id, approved);
|
|
686
|
+
}
|
|
687
|
+
|
|
676
688
|
getState() {
|
|
677
689
|
const state = this.#runtime.getRuntimeState();
|
|
678
690
|
const serializableState = typeof state?.toJSON === 'function' ? state.toJSON() : state;
|
|
@@ -691,33 +703,50 @@ export class RuntimeBridge {
|
|
|
691
703
|
return messages
|
|
692
704
|
.filter(m => m.role !== 'system')
|
|
693
705
|
.map(m => ({
|
|
694
|
-
role: m.role,
|
|
695
|
-
content: typeof m.content === 'string' ? m.content : (Array.isArray(m.content) ? m.content.map(c => c.text || '').join('') : ''),
|
|
696
|
-
reasoningContent: typeof m.reasoning_content === 'string' ? m.reasoning_content : '',
|
|
697
|
-
reasoningDetails: Array.isArray(m.reasoning_details) ? m.reasoning_details : [],
|
|
698
|
-
reasoningStartedAt: m.reasoning_started_at || null,
|
|
699
|
-
reasoningEndedAt: m.reasoning_ended_at || null,
|
|
700
|
-
reasoningDurationMs: Number.isFinite(Number(m.reasoning_duration_ms)) ? Number(m.reasoning_duration_ms) : null,
|
|
701
|
-
toolCalls: m.tool_calls || [],
|
|
702
|
-
fileChanges: Array.isArray(m.file_changes) ? m.file_changes : [],
|
|
703
|
-
toolCallId: m.tool_call_id || null,
|
|
704
|
-
toolSummary: m.role === 'tool' ? summarizeHistoricalToolMessage(m) : null,
|
|
706
|
+
role: m.role,
|
|
707
|
+
content: typeof m.content === 'string' ? m.content : (Array.isArray(m.content) ? m.content.map(c => c.text || '').join('') : ''),
|
|
708
|
+
reasoningContent: typeof m.reasoning_content === 'string' ? m.reasoning_content : '',
|
|
709
|
+
reasoningDetails: Array.isArray(m.reasoning_details) ? m.reasoning_details : [],
|
|
710
|
+
reasoningStartedAt: m.reasoning_started_at || null,
|
|
711
|
+
reasoningEndedAt: m.reasoning_ended_at || null,
|
|
712
|
+
reasoningDurationMs: Number.isFinite(Number(m.reasoning_duration_ms)) ? Number(m.reasoning_duration_ms) : null,
|
|
713
|
+
toolCalls: m.tool_calls || [],
|
|
714
|
+
fileChanges: Array.isArray(m.file_changes) ? m.file_changes : [],
|
|
715
|
+
toolCallId: m.tool_call_id || null,
|
|
716
|
+
toolSummary: m.role === 'tool' ? summarizeHistoricalToolMessage(m) : null,
|
|
705
717
|
toolDurationMs: Number.isFinite(Number(m.tool_duration_ms)) ? Number(m.tool_duration_ms) : null,
|
|
706
718
|
toolStatus: m.tool_status || null,
|
|
719
|
+
toolResultMeta: m.tool_result_meta || null,
|
|
707
720
|
toolFileChange: m.tool_file_change || null,
|
|
721
|
+
toolFileChanges: Array.isArray(m.tool_file_changes) ? m.tool_file_changes : [],
|
|
708
722
|
planTranscript: Array.isArray(m.plan_transcript) ? m.plan_transcript : null,
|
|
709
723
|
usage: normalizeUiUsage(m.usage),
|
|
710
724
|
at: m.at || null
|
|
711
725
|
}));
|
|
712
726
|
}
|
|
713
727
|
|
|
714
|
-
getSessionCompactMeta() {
|
|
728
|
+
getSessionCompactMeta() {
|
|
715
729
|
const compact = this.#runtime.getSessionCompact();
|
|
716
730
|
if (!compact) return null;
|
|
717
731
|
return { boundaryIndex: compact.boundaryIndex, mode: compact.mode, timestamp: compact.timestamp };
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
getChangeSets() {
|
|
735
|
+
return this.#runtime.getChangeSets?.() || [];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
getChangeSetPatch(id) {
|
|
739
|
+
return this.#runtime.getChangeSetPatch?.(id) || '';
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
async undoChangeSet(id) {
|
|
743
|
+
if (this.#busy) return { error: true, message: 'A request is already in progress' };
|
|
744
|
+
const result = await this.#runtime.undoChangeSet?.(id);
|
|
745
|
+
this.#broadcast({ type: 'change:undone', result });
|
|
746
|
+
return result || { error: true, message: 'Git change oplog is not available' };
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async getUiMessages() {
|
|
721
750
|
this.#resetUiTranscriptIfSessionChanged();
|
|
722
751
|
if (this.#uiMessages.length > 0) return this.#uiMessages;
|
|
723
752
|
const sessionId = this.getSessionId();
|