tsugite-claude-code 0.24.0__tar.gz → 0.25.0__tar.gz
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.
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/.gitignore +1 -0
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/PKG-INFO +2 -2
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/pyproject.toml +2 -2
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/tsugite_claude_code/process.py +1 -0
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/tsugite_claude_code/provider.py +22 -22
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/tsugite_claude_code/__init__.py +0 -0
- {tsugite_claude_code-0.24.0 → tsugite_claude_code-0.25.0}/tsugite_claude_code/usage.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tsugite-claude-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.0
|
|
4
4
|
Summary: Tsugite plugin: Claude Code provider (routes LLM calls through the claude CLI)
|
|
5
5
|
Author: Justyn Shull
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
|
-
Requires-Dist: tsugite-cli==0.
|
|
7
|
+
Requires-Dist: tsugite-cli==0.25.0
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tsugite-claude-code"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.25.0"
|
|
4
4
|
description = "Tsugite plugin: Claude Code provider (routes LLM calls through the claude CLI)"
|
|
5
5
|
authors = [{ name = "Justyn Shull" }]
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"tsugite-cli==0.
|
|
8
|
+
"tsugite-cli==0.25.0",
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
[project.entry-points."tsugite.providers"]
|
|
@@ -40,16 +40,12 @@ _CLAUDE_CODE_MODELS: dict[str, ModelInfo] = {
|
|
|
40
40
|
# --- END GENERATED MODELS (claude_code) --- #
|
|
41
41
|
|
|
42
42
|
# Bare tier aliases track the newest model in that tier; version-pinned aliases
|
|
43
|
-
# stay for reproducible configs.
|
|
44
|
-
# through Claude Code's resume path can leave an empty assistant block that wedges
|
|
45
|
-
# the sidecar transcript (400 "text content blocks must be non-empty"), and the
|
|
46
|
-
# fresh-session fallback only covers the first send, so opus-5 and opus-5-5 are
|
|
47
|
-
# opt-in via their pinned aliases until a mid-conversation poison recovers cleanly.
|
|
43
|
+
# stay for reproducible configs.
|
|
48
44
|
_ALIASES = {
|
|
49
45
|
"fable": "claude-fable-5-1",
|
|
50
46
|
"fable-5-1": "claude-fable-5-1",
|
|
51
47
|
"fable-5": "claude-fable-5",
|
|
52
|
-
"opus": "claude-opus-
|
|
48
|
+
"opus": "claude-opus-5-5",
|
|
53
49
|
"opus-5-5": "claude-opus-5-5",
|
|
54
50
|
"opus-5": "claude-opus-5",
|
|
55
51
|
"opus-4-8": "claude-opus-4-8",
|
|
@@ -57,7 +53,7 @@ _ALIASES = {
|
|
|
57
53
|
"opus-4-6": "claude-opus-4-6",
|
|
58
54
|
"opus-4-5": "claude-opus-4-5",
|
|
59
55
|
"opus-4-5-20251101": "claude-opus-4-5-20251101",
|
|
60
|
-
"sonnet": "claude-sonnet-5",
|
|
56
|
+
"sonnet": "claude-sonnet-5-5",
|
|
61
57
|
"sonnet-5-5": "claude-sonnet-5-5",
|
|
62
58
|
"sonnet-5": "claude-sonnet-5",
|
|
63
59
|
"sonnet-4-6": "claude-sonnet-4-6",
|
|
@@ -70,14 +66,6 @@ _ALIASES = {
|
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
|
|
73
|
-
def _is_unresumable_history_error(exc: BaseException) -> bool:
|
|
74
|
-
"""A 400 on a resume replay means the sidecar transcript itself is malformed
|
|
75
|
-
(e.g. an empty text content block) — retrying the same resume can never
|
|
76
|
-
succeed. Anything else (prompt too long, 429/529, execution errors) must
|
|
77
|
-
keep surfacing so the daemon's existing retry paths handle it."""
|
|
78
|
-
return "API Error: 400" in str(exc)
|
|
79
|
-
|
|
80
|
-
|
|
81
69
|
def _raise_if_error(result_event: dict) -> None:
|
|
82
70
|
"""Translate a Claude CLI error result into AgentExecutionError.
|
|
83
71
|
|
|
@@ -88,8 +76,14 @@ def _raise_if_error(result_event: dict) -> None:
|
|
|
88
76
|
"""
|
|
89
77
|
if not result_event.get("is_error"):
|
|
90
78
|
return
|
|
91
|
-
text =
|
|
79
|
+
text = (
|
|
80
|
+
result_event.get("text")
|
|
81
|
+
or "; ".join(result_event.get("errors") or [])
|
|
82
|
+
or "Claude Code returned an error result"
|
|
83
|
+
)
|
|
92
84
|
subtype = result_event.get("subtype") or "error"
|
|
85
|
+
if "Failed to authenticate" in text:
|
|
86
|
+
text = f"{text.rstrip('.')}. Run `claude /login` on the daemon host to sign in again."
|
|
93
87
|
raise AgentExecutionError(f"{text} (subtype={subtype})")
|
|
94
88
|
|
|
95
89
|
|
|
@@ -204,14 +198,20 @@ class ClaudeCodeProvider:
|
|
|
204
198
|
return await self._collect(retry_content)
|
|
205
199
|
|
|
206
200
|
def _should_fallback_to_fresh_session(self, first_turn: bool, err: BaseException) -> bool:
|
|
207
|
-
"""
|
|
208
|
-
|
|
201
|
+
"""A first-send 400 on a resume means the sidecar transcript itself is malformed
|
|
202
|
+
(e.g. an empty text content block), and a missing transcript was deleted by the
|
|
203
|
+
CLI. Retrying the same resume can never succeed. Anything else (prompt too long,
|
|
204
|
+
429/529, other execution errors) must keep surfacing so the daemon's retry paths
|
|
205
|
+
handle it."""
|
|
206
|
+
if not (first_turn and self._resume_session):
|
|
207
|
+
return False
|
|
208
|
+
message = str(err)
|
|
209
|
+
return "API Error: 400" in message or "No conversation found with session ID" in message
|
|
209
210
|
|
|
210
211
|
async def _fallback_to_fresh_session(self, err: BaseException) -> str:
|
|
211
|
-
"""Sever
|
|
212
|
-
seeded from tsugite's serialized history,
|
|
213
|
-
|
|
214
|
-
raw content was empty survives as a non-empty block."""
|
|
212
|
+
"""Sever an unresumable resume (poisoned or deleted transcript): replace the
|
|
213
|
+
subprocess with a fresh session seeded from tsugite's serialized history,
|
|
214
|
+
and return the rebuilt first message."""
|
|
215
215
|
from tsugite_claude_code.process import ClaudeCodeProcess
|
|
216
216
|
|
|
217
217
|
logger.warning(
|
|
File without changes
|
|
File without changes
|