@kaitranntt/ccs 3.4.4 → 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 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.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
- if (block.type === 'thinking') {
105
- // C-02 Fix: Enforce buffer size limit
106
- if (this.thinkingBuffer.length + delta.length > this.maxBufferSize) {
107
- throw new Error(`Thinking buffer exceeded ${this.maxBufferSize} bytes (DoS protection)`);
108
- }
109
- this.thinkingBuffer += delta;
110
- block.content = this.thinkingBuffer;
111
- } else if (block.type === 'text') {
112
- // C-02 Fix: Enforce buffer size limit
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/ (CCS_DEBUG_LOG=1)
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
- // Support both CCS_DEBUG and CCS_DEBUG_LOG (with deprecation warning)
41
- const oldVar = process.env.CCS_DEBUG_LOG === '1';
42
- const newVar = process.env.CCS_DEBUG === '1';
43
- this.debugLog = config.debugLog ?? (newVar || oldVar);
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
- events.push(this._createSignatureDeltaEvent(currentBlock));
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
- events.push(this._createSignatureDeltaEvent(currentBlock));
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
- events.push(this._createSignatureDeltaEvent(currentBlock));
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.3"
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.3"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "3.4.4",
3
+ "version": "3.4.5",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
5
5
  "keywords": [
6
6
  "cli",