@kaitranntt/ccs 3.4.3 → 3.4.5
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/README.md +18 -0
- package/VERSION +1 -1
- package/bin/glmt/delta-accumulator.js +30 -15
- package/bin/glmt/glmt-transformer.js +43 -14
- package/lib/ccs +1 -1
- package/lib/ccs.ps1 +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +1 -1
package/README.md
CHANGED
|
@@ -199,6 +199,24 @@ Commands and skills symlinked from `~/.ccs/shared/` - no duplication across prof
|
|
|
199
199
|
|
|
200
200
|
> **[!] Important**: GLMT requires npm installation (`npm install -g @kaitranntt/ccs`). Not available in native shell versions (requires Node.js HTTP server).
|
|
201
201
|
|
|
202
|
+
### Acknowledgments: The Foundation That Made GLMT Possible
|
|
203
|
+
|
|
204
|
+
> **[i] Pioneering Work by [@Bedolla](https://github.com/Bedolla)**
|
|
205
|
+
>
|
|
206
|
+
> **CCS's GLMT implementation owes its existence to the groundbreaking work of [@Bedolla](https://github.com/Bedolla)**, who created [ZaiTransformer](https://github.com/Bedolla/ZaiTransformer/) - the **first integration** to bridge [Claude Code Router (CCR)](https://github.com/musistudio/claude-code-router) with Z.AI's reasoning capabilities.
|
|
207
|
+
>
|
|
208
|
+
> **Why this matters**: Before ZaiTransformer, no one had successfully integrated Z.AI's thinking mode with Claude Code's workflow. Bedolla's work wasn't just helpful - it was **foundational**. His implementation of:
|
|
209
|
+
>
|
|
210
|
+
> - **Request/response transformation architecture** - The conceptual blueprint for how to bridge Anthropic and OpenAI formats
|
|
211
|
+
> - **Thinking mode control mechanisms** - The patterns for managing reasoning_content delivery
|
|
212
|
+
> - **Embedded proxy design** - The architecture that CCS's GLMT proxy is built upon
|
|
213
|
+
>
|
|
214
|
+
> These contributions directly inspired and enabled GLMT's design. **Without ZaiTransformer's pioneering work, GLMT wouldn't exist in its current form**. The technical patterns, transformation logic, and proxy architecture implemented in CCS are a direct evolution of the concepts Bedolla first proved viable.
|
|
215
|
+
>
|
|
216
|
+
> **Recognition**: If you benefit from GLMT's thinking capabilities, you're benefiting from Bedolla's vision and engineering. Please consider starring [ZaiTransformer](https://github.com/Bedolla/ZaiTransformer/) to support pioneering work in the Claude Code ecosystem.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
202
220
|
### GLM vs GLMT
|
|
203
221
|
|
|
204
222
|
| Feature | GLM (`ccs glm`) | GLMT (`ccs glmt`) |
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.4.
|
|
1
|
+
3.4.5
|
|
@@ -100,22 +100,32 @@ class DeltaAccumulator {
|
|
|
100
100
|
*/
|
|
101
101
|
addDelta(delta) {
|
|
102
102
|
const block = this.getCurrentBlock();
|
|
103
|
-
if (block) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (this.textBuffer.length + delta.length > this.maxBufferSize) {
|
|
114
|
-
throw new Error(`Text buffer exceeded ${this.maxBufferSize} bytes (DoS protection)`);
|
|
115
|
-
}
|
|
116
|
-
this.textBuffer += delta;
|
|
117
|
-
block.content = this.textBuffer;
|
|
103
|
+
if (!block) {
|
|
104
|
+
// FIX: Guard against null block (should never happen, but defensive)
|
|
105
|
+
console.error('[DeltaAccumulator] ERROR: addDelta called with no current block');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (block.type === 'thinking') {
|
|
110
|
+
// C-02 Fix: Enforce buffer size limit
|
|
111
|
+
if (this.thinkingBuffer.length + delta.length > this.maxBufferSize) {
|
|
112
|
+
throw new Error(`Thinking buffer exceeded ${this.maxBufferSize} bytes (DoS protection)`);
|
|
118
113
|
}
|
|
114
|
+
this.thinkingBuffer += delta;
|
|
115
|
+
block.content = this.thinkingBuffer;
|
|
116
|
+
|
|
117
|
+
// FIX: Verify assignment succeeded (paranoid check for race conditions)
|
|
118
|
+
if (block.content.length !== this.thinkingBuffer.length) {
|
|
119
|
+
console.error('[DeltaAccumulator] ERROR: Block content assignment failed');
|
|
120
|
+
console.error(`Expected: ${this.thinkingBuffer.length}, Got: ${block.content.length}`);
|
|
121
|
+
}
|
|
122
|
+
} else if (block.type === 'text') {
|
|
123
|
+
// C-02 Fix: Enforce buffer size limit
|
|
124
|
+
if (this.textBuffer.length + delta.length > this.maxBufferSize) {
|
|
125
|
+
throw new Error(`Text buffer exceeded ${this.maxBufferSize} bytes (DoS protection)`);
|
|
126
|
+
}
|
|
127
|
+
this.textBuffer += delta;
|
|
128
|
+
block.content = this.textBuffer;
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
|
|
@@ -126,6 +136,11 @@ class DeltaAccumulator {
|
|
|
126
136
|
const block = this.getCurrentBlock();
|
|
127
137
|
if (block) {
|
|
128
138
|
block.stopped = true;
|
|
139
|
+
|
|
140
|
+
// FIX: Log block closure for debugging (helps diagnose timing issues)
|
|
141
|
+
if (block.type === 'thinking' && process.env.CCS_DEBUG === '1') {
|
|
142
|
+
console.error(`[DeltaAccumulator] Stopped thinking block ${block.index}: ${block.content?.length || 0} chars`);
|
|
143
|
+
}
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
|
|
@@ -17,7 +17,7 @@ const LocaleEnforcer = require('./locale-enforcer');
|
|
|
17
17
|
* - Response: OpenAI reasoning_content → Anthropic thinking blocks
|
|
18
18
|
* - Tool Support: Anthropic tools ↔ OpenAI function calling (bidirectional)
|
|
19
19
|
* - Streaming: Real-time tool calls with input_json deltas
|
|
20
|
-
* - Debug mode: Log raw data to ~/.ccs/logs/ (
|
|
20
|
+
* - Debug mode: Log raw data to ~/.ccs/logs/ (CCS_DEBUG=1)
|
|
21
21
|
* - Verbose mode: Console logging with timestamps
|
|
22
22
|
* - Validation: Self-test transformation results
|
|
23
23
|
*
|
|
@@ -37,16 +37,10 @@ class GlmtTransformer {
|
|
|
37
37
|
this.defaultThinking = config.defaultThinking ?? true;
|
|
38
38
|
this.verbose = config.verbose || false;
|
|
39
39
|
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
// Show deprecation warning once
|
|
46
|
-
if (oldVar && !newVar && !GlmtTransformer._warnedDeprecation) {
|
|
47
|
-
console.warn('[glmt] Warning: CCS_DEBUG_LOG is deprecated, use CCS_DEBUG instead');
|
|
48
|
-
GlmtTransformer._warnedDeprecation = true;
|
|
49
|
-
}
|
|
40
|
+
// CCS_DEBUG controls all debug logging (file + console)
|
|
41
|
+
const debugEnabled = process.env.CCS_DEBUG === '1';
|
|
42
|
+
this.debugLog = config.debugLog ?? debugEnabled;
|
|
43
|
+
this.debugMode = config.debugMode ?? debugEnabled;
|
|
50
44
|
|
|
51
45
|
this.debugLogDir = config.debugLogDir || path.join(os.homedir(), '.ccs', 'logs');
|
|
52
46
|
this.modelMaxTokens = {
|
|
@@ -645,10 +639,20 @@ class GlmtTransformer {
|
|
|
645
639
|
if (delta.reasoning_content) {
|
|
646
640
|
const currentBlock = accumulator.getCurrentBlock();
|
|
647
641
|
|
|
642
|
+
// FIX: Enhanced debug logging for thinking block diagnostics
|
|
643
|
+
if (this.debugMode) {
|
|
644
|
+
console.error(`[GLMT-DEBUG] Reasoning delta: ${delta.reasoning_content.length} chars`);
|
|
645
|
+
console.error(`[GLMT-DEBUG] Current block: ${currentBlock?.type || 'none'}, index: ${currentBlock?.index ?? 'N/A'}`);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
648
|
if (!currentBlock || currentBlock.type !== 'thinking') {
|
|
649
649
|
// Start thinking block
|
|
650
650
|
const block = accumulator.startBlock('thinking');
|
|
651
651
|
events.push(this._createContentBlockStartEvent(block));
|
|
652
|
+
|
|
653
|
+
if (this.debugMode) {
|
|
654
|
+
console.error(`[GLMT-DEBUG] Started new thinking block ${block.index}`);
|
|
655
|
+
}
|
|
652
656
|
}
|
|
653
657
|
|
|
654
658
|
accumulator.addDelta(delta.reasoning_content);
|
|
@@ -664,7 +668,10 @@ class GlmtTransformer {
|
|
|
664
668
|
|
|
665
669
|
// Close thinking block if transitioning from thinking to text
|
|
666
670
|
if (currentBlock && currentBlock.type === 'thinking' && !currentBlock.stopped) {
|
|
667
|
-
|
|
671
|
+
const signatureEvent = this._createSignatureDeltaEvent(currentBlock);
|
|
672
|
+
if (signatureEvent) { // FIX: Handle null return from signature race guard
|
|
673
|
+
events.push(signatureEvent);
|
|
674
|
+
}
|
|
668
675
|
events.push(this._createContentBlockStopEvent(currentBlock));
|
|
669
676
|
accumulator.stopCurrentBlock();
|
|
670
677
|
}
|
|
@@ -691,7 +698,10 @@ class GlmtTransformer {
|
|
|
691
698
|
const currentBlock = accumulator.getCurrentBlock();
|
|
692
699
|
if (currentBlock && !currentBlock.stopped) {
|
|
693
700
|
if (currentBlock.type === 'thinking') {
|
|
694
|
-
|
|
701
|
+
const signatureEvent = this._createSignatureDeltaEvent(currentBlock);
|
|
702
|
+
if (signatureEvent) { // FIX: Handle null return from signature race guard
|
|
703
|
+
events.push(signatureEvent);
|
|
704
|
+
}
|
|
695
705
|
}
|
|
696
706
|
events.push(this._createContentBlockStopEvent(currentBlock));
|
|
697
707
|
accumulator.stopCurrentBlock();
|
|
@@ -794,7 +804,10 @@ class GlmtTransformer {
|
|
|
794
804
|
const currentBlock = accumulator.getCurrentBlock();
|
|
795
805
|
if (currentBlock && !currentBlock.stopped) {
|
|
796
806
|
if (currentBlock.type === 'thinking') {
|
|
797
|
-
|
|
807
|
+
const signatureEvent = this._createSignatureDeltaEvent(currentBlock);
|
|
808
|
+
if (signatureEvent) { // FIX: Handle null return from signature race guard
|
|
809
|
+
events.push(signatureEvent);
|
|
810
|
+
}
|
|
798
811
|
}
|
|
799
812
|
events.push(this._createContentBlockStopEvent(currentBlock));
|
|
800
813
|
accumulator.stopCurrentBlock();
|
|
@@ -914,7 +927,23 @@ class GlmtTransformer {
|
|
|
914
927
|
* @private
|
|
915
928
|
*/
|
|
916
929
|
_createSignatureDeltaEvent(block) {
|
|
930
|
+
// FIX: Guard against empty content (signature timing race)
|
|
931
|
+
// In streaming mode, signature may be requested before content fully accumulated
|
|
932
|
+
if (!block.content || block.content.length === 0) {
|
|
933
|
+
if (this.verbose) {
|
|
934
|
+
this.log(`WARNING: Skipping signature for empty thinking block ${block.index}`);
|
|
935
|
+
this.log(`This indicates a race condition - signature requested before content accumulated`);
|
|
936
|
+
}
|
|
937
|
+
return null; // Return null instead of event
|
|
938
|
+
}
|
|
939
|
+
|
|
917
940
|
const signature = this._generateThinkingSignature(block.content);
|
|
941
|
+
|
|
942
|
+
// Enhanced logging for debugging
|
|
943
|
+
if (this.verbose) {
|
|
944
|
+
this.log(`Generating signature for block ${block.index}: ${block.content.length} chars`);
|
|
945
|
+
}
|
|
946
|
+
|
|
918
947
|
return {
|
|
919
948
|
event: 'content_block_delta',
|
|
920
949
|
data: {
|
package/lib/ccs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
# Version (updated by scripts/bump-version.sh)
|
|
5
|
-
CCS_VERSION="3.4.
|
|
5
|
+
CCS_VERSION="3.4.5"
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
7
|
readonly CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}"
|
|
8
8
|
readonly PROFILES_JSON="$HOME/.ccs/profiles.json"
|
package/lib/ccs.ps1
CHANGED
|
@@ -12,7 +12,7 @@ param(
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
13
|
|
|
14
14
|
# Version (updated by scripts/bump-version.sh)
|
|
15
|
-
$CcsVersion = "3.4.
|
|
15
|
+
$CcsVersion = "3.4.5"
|
|
16
16
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
17
17
|
$ConfigFile = if ($env:CCS_CONFIG) { $env:CCS_CONFIG } else { "$env:USERPROFILE\.ccs\config.json" }
|
|
18
18
|
$ProfilesJson = "$env:USERPROFILE\.ccs\profiles.json"
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -93,7 +93,7 @@ function createConfigFiles() {
|
|
|
93
93
|
// Migrate from v3.1.1 to v3.2.0 (symlink architecture)
|
|
94
94
|
console.log('');
|
|
95
95
|
try {
|
|
96
|
-
const SharedManager = require('../bin/shared-manager');
|
|
96
|
+
const SharedManager = require('../bin/management/shared-manager');
|
|
97
97
|
const sharedManager = new SharedManager();
|
|
98
98
|
sharedManager.migrateFromV311();
|
|
99
99
|
sharedManager.ensureSharedDirectories();
|