amicus 1.5.0 → 1.5.1
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/sidecar/conversation-mirror.js +27 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"author": { "name": "Christian Wagner" },
|
|
6
6
|
"homepage": "https://bourbondog.github.io/amicus/",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to Amicus are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.5.1] - 2026-06-29
|
|
9
|
+
|
|
10
|
+
A headless-reliability fix for reasoning-heavy models.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Gemini (and other reasoning-only models) no longer hang headless with "No Output."** On the
|
|
14
|
+
direct-Google provider path, Gemini 3.x returns its answer as a `reasoning` part with no separate
|
|
15
|
+
`text` part. The conversation mirror only accumulated `text` parts, so it captured zero output, the
|
|
16
|
+
headless completion gates (which key on `output.length > 0`) never fired, and the run burned the full
|
|
17
|
+
timeout — while still billing input/thinking tokens. The mirror now accumulates reasoning into a
|
|
18
|
+
dedicated buffer and promotes it to the output **only when a finished assistant message produced no
|
|
19
|
+
visible text**, so models that emit both a reasoning part and a text part are unaffected (their
|
|
20
|
+
thinking never pollutes the answer). Fixes `--model gemini` / `gemini-pro` and any direct `google/*`
|
|
21
|
+
alias in headless `start`, `fanout`, and council runs.
|
|
22
|
+
|
|
8
23
|
## [1.5.0] - 2026-06-29
|
|
9
24
|
|
|
10
25
|
A visual refresh plus a council-reliability fix and a config-dir consolidation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -17,6 +17,8 @@ function createMirrorState() {
|
|
|
17
17
|
seenToolResultIds: new Set(),
|
|
18
18
|
receivingReported: false,
|
|
19
19
|
output: '', // accumulated assistant text
|
|
20
|
+
seenReasoningParts: new Map(), // partId -> last captured reasoning length
|
|
21
|
+
reasoningOutput: '', // accumulated reasoning text (promoted to output only if no text part arrives)
|
|
20
22
|
usageByMsg: new Map(), // msgId -> {tokens, cost}
|
|
21
23
|
};
|
|
22
24
|
}
|
|
@@ -101,6 +103,22 @@ function mirrorMessages(messages, state, opts = {}) {
|
|
|
101
103
|
timestamp: now(),
|
|
102
104
|
});
|
|
103
105
|
}
|
|
106
|
+
} else if (part.type === 'reasoning' && part.text) {
|
|
107
|
+
// Some providers (e.g. Gemini 3.x on the direct Google path) return the
|
|
108
|
+
// answer as a reasoning part with no separate text part. Accumulate it in a
|
|
109
|
+
// dedicated buffer; it is promoted to `output` at finalization ONLY when no
|
|
110
|
+
// visible text part ever arrives (see below), so models that emit BOTH a
|
|
111
|
+
// reasoning part and a text part are unaffected (their thinking never
|
|
112
|
+
// pollutes the answer).
|
|
113
|
+
const prevLen = state.seenReasoningParts.get(partId) || 0;
|
|
114
|
+
if (part.text.length > prevLen) {
|
|
115
|
+
state.reasoningOutput += part.text.slice(prevLen);
|
|
116
|
+
state.seenReasoningParts.set(partId, part.text.length);
|
|
117
|
+
if (!state.receivingReported) {
|
|
118
|
+
state.receivingReported = true;
|
|
119
|
+
progressUpdates.push({ stage: 'receiving', extra: { messagesReceived: 1 } });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
124
|
}
|
|
@@ -110,6 +128,15 @@ function mirrorMessages(messages, state, opts = {}) {
|
|
|
110
128
|
const lastAssistant = list.filter(m => m.info && m.info.role === 'assistant').pop();
|
|
111
129
|
assistantFinished = !!(lastAssistant && lastAssistant.info.time && lastAssistant.info.time.completed);
|
|
112
130
|
|
|
131
|
+
// Reasoning-only fallback: if the assistant finished but emitted only reasoning
|
|
132
|
+
// parts (no visible text), promote the reasoning text to `output` so the headless
|
|
133
|
+
// completion gates fire and the answer isn't lost as "No Output". Runs once — once
|
|
134
|
+
// `output` is non-empty this is skipped on subsequent polls.
|
|
135
|
+
if (assistantFinished && !state.output && state.reasoningOutput) {
|
|
136
|
+
state.output = state.reasoningOutput;
|
|
137
|
+
appendLines.push({ role: 'assistant', content: state.reasoningOutput, timestamp: now() });
|
|
138
|
+
}
|
|
139
|
+
|
|
113
140
|
return { appendLines, progressUpdates, state, currentAssistantMsgId, assistantFinished, sessionError, messageCount };
|
|
114
141
|
}
|
|
115
142
|
|