@hifullmoon/aicommit 2.6.1 → 2.6.2
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/CHANGELOG.md +8 -1
- package/docs/distribution.md +1 -1
- package/package.json +1 -1
- package/src/generation-ui.js +11 -2
- package/src/model-client.js +1 -1
- package/src/provider-response.js +41 -4
- package/src/ui.js +13 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.6.2] - 2026-09-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- MiniMax streaming now accepts both cumulative snapshots and ordinary deltas without duplicating thinking or corrupting split plans. The live thinking panel also updates batch progress after the spinner stops.
|
|
12
|
+
|
|
7
13
|
## [2.6.1] - 2026-09-14
|
|
8
14
|
|
|
9
15
|
### Fixed
|
|
@@ -239,7 +245,8 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
239
245
|
- Added file-level split planning and execution with Git-state concurrency checks.
|
|
240
246
|
- Added provider presets and user/project configuration boundaries.
|
|
241
247
|
|
|
242
|
-
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.
|
|
248
|
+
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.2...HEAD
|
|
249
|
+
[2.6.2]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.2
|
|
243
250
|
[2.6.1]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.1
|
|
244
251
|
[2.6.0]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.0
|
|
245
252
|
[2.5.0]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.5.0
|
package/docs/distribution.md
CHANGED
|
@@ -33,7 +33,7 @@ The release workflow uses npm Trusted Publishing without a long-lived `NPM_TOKEN
|
|
|
33
33
|
```bash
|
|
34
34
|
workdir=$(mktemp -d)
|
|
35
35
|
cd "$workdir"
|
|
36
|
-
npm install --package-lock-only @hifullmoon/aicommit@2.6.
|
|
36
|
+
npm install --package-lock-only @hifullmoon/aicommit@2.6.2
|
|
37
37
|
npm audit signatures
|
|
38
38
|
```
|
|
39
39
|
|
package/package.json
CHANGED
package/src/generation-ui.js
CHANGED
|
@@ -17,16 +17,25 @@ export async function runModelTask({
|
|
|
17
17
|
}) {
|
|
18
18
|
const spinner = ora({ text: chalk.dim(spinnerText), color: 'cyan' }).start();
|
|
19
19
|
let liveReasoning;
|
|
20
|
+
let progressStatus = 'Thinking…';
|
|
21
|
+
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
20
22
|
const stream =
|
|
21
23
|
reasoning && reasoning.mode !== 'off' && !machineOutput
|
|
22
24
|
? {
|
|
23
25
|
onProgress(status) {
|
|
24
|
-
|
|
26
|
+
progressStatus = status;
|
|
27
|
+
if (liveReasoning) liveReasoning.setProgress(status);
|
|
28
|
+
else if (spinner.isSpinning) spinner.text = chalk.dim(status);
|
|
25
29
|
},
|
|
26
30
|
onReasoningDelta(chunk) {
|
|
31
|
+
if (!interactive) return;
|
|
27
32
|
if (!liveReasoning) {
|
|
28
33
|
spinner.stop();
|
|
29
|
-
liveReasoning = startReasoningStream(
|
|
34
|
+
liveReasoning = startReasoningStream(
|
|
35
|
+
reasoning.maxDisplayChars,
|
|
36
|
+
chunk,
|
|
37
|
+
progressStatus,
|
|
38
|
+
);
|
|
30
39
|
return;
|
|
31
40
|
}
|
|
32
41
|
liveReasoning.append(chunk);
|
package/src/model-client.js
CHANGED
|
@@ -236,7 +236,7 @@ function transport(config, adapter, state) {
|
|
|
236
236
|
if (
|
|
237
237
|
(response.headers.get('content-type') || '').toLowerCase().includes('text/event-stream')
|
|
238
238
|
)
|
|
239
|
-
return normalizeEventStream(response);
|
|
239
|
+
return normalizeEventStream(response, adapter.id);
|
|
240
240
|
let data;
|
|
241
241
|
try {
|
|
242
242
|
data = await response.json();
|
package/src/provider-response.js
CHANGED
|
@@ -89,14 +89,50 @@ function reasoningDelta(delta) {
|
|
|
89
89
|
return primary + details;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
// MiniMax may stream ordinary deltas or the cumulative snapshots shown in its
|
|
93
|
+
// OpenAI-compatible example. Detect a snapshot only when it extends
|
|
94
|
+
// the text already received; otherwise retain the chunk verbatim so repeated
|
|
95
|
+
// ordinary deltas are not lost.
|
|
96
|
+
function miniMaxIncrement(value, state) {
|
|
97
|
+
if (typeof value !== 'string' || !value) return value;
|
|
98
|
+
const prior = state.text;
|
|
99
|
+
if (prior && value.startsWith(prior)) {
|
|
100
|
+
if (value.length > prior.length) {
|
|
101
|
+
state.text = value;
|
|
102
|
+
state.cumulative = true;
|
|
103
|
+
return value.slice(prior.length);
|
|
104
|
+
}
|
|
105
|
+
if (state.cumulative) return '';
|
|
106
|
+
}
|
|
107
|
+
state.text += value;
|
|
108
|
+
state.cumulative = false;
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function normalizeChunk(value, miniMaxChoices = null) {
|
|
93
113
|
if (!value || !Array.isArray(value.choices)) return value;
|
|
94
114
|
for (const choice of value.choices) {
|
|
95
115
|
if (!choice || typeof choice !== 'object') continue;
|
|
96
116
|
choice.finish_reason = normalizeFinishReason(choice.finish_reason);
|
|
97
117
|
const delta = choice.delta ?? choice.message;
|
|
98
118
|
if (!delta || typeof delta !== 'object') continue;
|
|
99
|
-
|
|
119
|
+
const reasoning = reasoningDelta(delta);
|
|
120
|
+
if (miniMaxChoices) {
|
|
121
|
+
const index = choice.index ?? 0;
|
|
122
|
+
if (!miniMaxChoices.has(index))
|
|
123
|
+
miniMaxChoices.set(index, {
|
|
124
|
+
content: { text: '', cumulative: false },
|
|
125
|
+
reasoning: { text: '', cumulative: false },
|
|
126
|
+
});
|
|
127
|
+
const state = miniMaxChoices.get(index);
|
|
128
|
+
choice.delta = {
|
|
129
|
+
...delta,
|
|
130
|
+
content: miniMaxIncrement(delta.content, state.content),
|
|
131
|
+
reasoning_content: miniMaxIncrement(reasoning, state.reasoning),
|
|
132
|
+
};
|
|
133
|
+
} else {
|
|
134
|
+
choice.delta = { ...delta, reasoning_content: reasoning };
|
|
135
|
+
}
|
|
100
136
|
// Retain reasoning_details for Pi's replay metadata while also exposing all
|
|
101
137
|
// its textual segments as ordinary thinking deltas, including legacy shapes.
|
|
102
138
|
}
|
|
@@ -106,10 +142,11 @@ function normalizeChunk(value) {
|
|
|
106
142
|
// eventsource-parser handles UTF-8-decoded SSE framing; this boundary adjusts
|
|
107
143
|
// only vendor fields. Pi still owns model-result assembly and finish validation.
|
|
108
144
|
// Web Stream piping preserves backpressure, cancellation and body read errors.
|
|
109
|
-
export function normalizeEventStream(response) {
|
|
145
|
+
export function normalizeEventStream(response, provider = '') {
|
|
110
146
|
if (!response.body)
|
|
111
147
|
throw fail(ERROR_CATEGORIES.RESPONSE_FORMAT, 'Streaming response did not include a body.');
|
|
112
148
|
let done = false;
|
|
149
|
+
const miniMaxChoices = provider === 'minimax' ? new Map() : null;
|
|
113
150
|
const encoder = new globalThis.TextEncoder();
|
|
114
151
|
const body = response.body
|
|
115
152
|
.pipeThrough(new globalThis.TextDecoderStream())
|
|
@@ -132,7 +169,7 @@ export function normalizeEventStream(response) {
|
|
|
132
169
|
{ cause },
|
|
133
170
|
);
|
|
134
171
|
}
|
|
135
|
-
data = JSON.stringify(normalizeChunk(value));
|
|
172
|
+
data = JSON.stringify(normalizeChunk(value, miniMaxChoices));
|
|
136
173
|
}
|
|
137
174
|
controller.enqueue(
|
|
138
175
|
encoder.encode(`${event.event ? `event: ${event.event}\n` : ''}data: ${data}\n\n`),
|
package/src/ui.js
CHANGED
|
@@ -169,6 +169,7 @@ function expandedLineCapacity(reservedRows) {
|
|
|
169
169
|
|
|
170
170
|
const reasoningStreamPrompt = createPrompt((config, done) => {
|
|
171
171
|
const [text, setText] = useState(config.initialText || '');
|
|
172
|
+
const [progressText, setProgressText] = useState(config.initialProgress || 'Thinking…');
|
|
172
173
|
const [expanded, setExpanded] = useState(false);
|
|
173
174
|
const [reasoningOffset, setReasoningOffset] = useState(0);
|
|
174
175
|
const [status, setStatus] = useState('streaming');
|
|
@@ -178,6 +179,7 @@ const reasoningStreamPrompt = createPrompt((config, done) => {
|
|
|
178
179
|
const maxOffset = Math.max(0, completeView.totalLines - pageSize);
|
|
179
180
|
|
|
180
181
|
config.controller.update = setText;
|
|
182
|
+
config.controller.updateProgress = setProgressText;
|
|
181
183
|
config.controller.finish = () => {
|
|
182
184
|
setStatus('done');
|
|
183
185
|
done(text);
|
|
@@ -220,14 +222,18 @@ const reasoningStreamPrompt = createPrompt((config, done) => {
|
|
|
220
222
|
maxExpandedLines: pageSize,
|
|
221
223
|
offset: reasoningOffset,
|
|
222
224
|
});
|
|
223
|
-
return `${panel}\n${chalk.dim(
|
|
225
|
+
return `${panel}\n${chalk.dim(` ${sanitizeTerminalText(progressText)} Ctrl+C cancel`)}\x1B[?25l`;
|
|
224
226
|
});
|
|
225
227
|
|
|
226
228
|
// Start a redraw-safe live reasoning view. Updates are batched to one frame
|
|
227
229
|
// every ~32ms so token-sized SSE chunks do not cause hundreds of terminal
|
|
228
230
|
// redraws per second. The complete text remains available to the subsequent
|
|
229
231
|
// review prompt after this temporary view is cleared.
|
|
230
|
-
export function startReasoningStream(
|
|
232
|
+
export function startReasoningStream(
|
|
233
|
+
maxChars = 12000,
|
|
234
|
+
initialText = '',
|
|
235
|
+
initialProgress = 'Thinking…',
|
|
236
|
+
) {
|
|
231
237
|
const controller = {};
|
|
232
238
|
let accumulated = String(initialText || '');
|
|
233
239
|
let timer = null;
|
|
@@ -238,6 +244,7 @@ export function startReasoningStream(maxChars = 12000, initialText = '') {
|
|
|
238
244
|
append(chunk) {
|
|
239
245
|
accumulated += String(chunk || '');
|
|
240
246
|
},
|
|
247
|
+
setProgress() {},
|
|
241
248
|
async stop() {
|
|
242
249
|
stopped = true;
|
|
243
250
|
return accumulated;
|
|
@@ -246,7 +253,7 @@ export function startReasoningStream(maxChars = 12000, initialText = '') {
|
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
const prompt = reasoningStreamPrompt(
|
|
249
|
-
{ controller, maxChars, initialText: accumulated },
|
|
256
|
+
{ controller, maxChars, initialText: accumulated, initialProgress },
|
|
250
257
|
{ clearPromptOnDone: true },
|
|
251
258
|
);
|
|
252
259
|
|
|
@@ -256,6 +263,9 @@ export function startReasoningStream(maxChars = 12000, initialText = '') {
|
|
|
256
263
|
};
|
|
257
264
|
|
|
258
265
|
return {
|
|
266
|
+
setProgress(status) {
|
|
267
|
+
if (!stopped) controller.updateProgress?.(status);
|
|
268
|
+
},
|
|
259
269
|
append(chunk) {
|
|
260
270
|
if (stopped || !chunk) return;
|
|
261
271
|
accumulated += String(chunk);
|