mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
chore: drop external project name references from code comments
This commit is contained in:
parent
a828de1306
commit
db16ff02a5
@ -26,7 +26,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 会话历史上下文窗口管理器(Hermes 风格升级版)
|
||||
* 会话历史上下文窗口管理器(四阶段压缩升级版)
|
||||
* <p>
|
||||
* 四阶段压缩策略:
|
||||
* <ol>
|
||||
@ -274,7 +274,7 @@ public class ConversationWindowManager {
|
||||
}
|
||||
int historyBudget = effectiveMax - reservedTokens;
|
||||
|
||||
// 尾部保护 token 预算:阈值的 20%(与 Hermes 一致)
|
||||
// 尾部保护 token 预算:阈值的 20%
|
||||
int tailTokenBudget = (int) (triggerThreshold * 0.20);
|
||||
|
||||
return compactMessages(messages, historyBudget, tailTokenBudget, chatModel,
|
||||
|
||||
@ -772,7 +772,7 @@ public class NodeStreamingChatHelper {
|
||||
boolean broadcast, int attempt) {
|
||||
if (attempt > 0) {
|
||||
long delay = Math.min(BACKOFF_BASE_MS * (1L << (attempt - 1)), BACKOFF_CAP_MS);
|
||||
// 加入 jitter 防止雷群效应(Hermes 风格)
|
||||
// 加入 jitter 防止雷群效应
|
||||
delay += ThreadLocalRandom.current().nextLong(0, Math.max(1, delay / 2));
|
||||
delay = Math.min(delay, BACKOFF_CAP_MS);
|
||||
log.warn("[{}] Retry attempt {}/{} after {}ms for conversation {}",
|
||||
@ -813,7 +813,7 @@ public class NodeStreamingChatHelper {
|
||||
AtomicInteger cacheWriteTokens = new AtomicInteger(0);
|
||||
|
||||
// thinking-only soft cap 触发后设为 true,外层轮询线程据此 dispose 订阅。
|
||||
// 注意:内容流的字符级 / 句子级重复检测已整体移除(参考 Hermes 思路:
|
||||
// 注意:内容流的字符级 / 句子级重复检测已整体移除(设计取舍:
|
||||
// agent 不替模型审核输出退化,靠 max_tokens + max_iterations 兜底);
|
||||
// 仅保留 thinking-only 这条体积兜底,处理 volcengine-plan 等 provider
|
||||
// 在 thinking 通道堆字符不出 content 的死循环(生产 trace c1eefa45)。
|
||||
|
||||
@ -72,7 +72,7 @@ public class ObservationNode implements NodeAction {
|
||||
// 合并为单条观察记录
|
||||
String combinedObservation = String.join("\n---\n", processedObservations);
|
||||
|
||||
// Budget Pressure Warning(Hermes 风格):接近上限时注入警告到工具结果中
|
||||
// Budget Pressure Warning:接近上限时注入警告到工具结果中
|
||||
// LLM 下一轮 reasoning 时能看到,从而主动收束,而非被硬性截断
|
||||
if (maxIterations > 0) {
|
||||
int progress = (int) ((double) nextIteration / maxIterations * 100);
|
||||
|
||||
@ -83,7 +83,7 @@ public class ReasoningNode implements NodeAction {
|
||||
private static final int DASHSCOPE_MAX_OUTPUT_TOKENS = 8192;
|
||||
|
||||
/**
|
||||
* Hermes-agent style enforcement clause appended to every ReasoningNode
|
||||
* Tool-use enforcement clause appended to every ReasoningNode
|
||||
* system prompt. Treats narration ("I will now …") as a protocol violation
|
||||
* to prevent the recurring failure mode where a model says it will call a
|
||||
* tool but emits the description as final_answer text instead.
|
||||
|
||||
@ -24,7 +24,7 @@ public class ConversationWindowProperties {
|
||||
/** 摘要自身最大 token 数(仅作 LLM maxToken 参数上限,实际预算由动态计算) */
|
||||
private int summaryMaxTokens = 800;
|
||||
|
||||
// ==================== 动态压缩配置(Hermes 风格) ====================
|
||||
// ==================== 动态压缩配置 ====================
|
||||
|
||||
/** 尾部保护的最小消息数(即使 token 预算用完也至少保留这么多) */
|
||||
private int protectLastMinMessages = 10;
|
||||
|
||||
@ -11,8 +11,6 @@ import java.util.List;
|
||||
*
|
||||
* <p>OAuth requests get extra beta headers + a User-Agent that masquerades as
|
||||
* Claude Code. Without these, Anthropic's infrastructure intermittently 500s.
|
||||
* Reference: hermes-agent {@code anthropic_adapter} lines 226-238 + the
|
||||
* dispatch in {@code build_anthropic_client} (line 423-433).
|
||||
*
|
||||
* <h2>Header reference</h2>
|
||||
*
|
||||
@ -48,8 +46,7 @@ public class ClaudeCodeApiHeaders {
|
||||
|
||||
/**
|
||||
* Comma-joined beta header list to send in {@code anthropic-beta}.
|
||||
* <p>Order matches hermes-agent {@code anthropic_adapter} line 427:
|
||||
* {@code common_betas + _OAUTH_ONLY_BETAS} — common betas first, OAuth betas appended.
|
||||
* <p>Order is common betas first, OAuth-only betas appended.
|
||||
*/
|
||||
public String allBetas() {
|
||||
return String.join(",",
|
||||
@ -61,14 +58,12 @@ public class ClaudeCodeApiHeaders {
|
||||
* Format: {@code claude-cli/<version>} — bare, no suffix.
|
||||
*
|
||||
* <p><b>History note:</b> we previously appended {@code (external, cli)}
|
||||
* after hermes-agent's pattern. That turned out to be wrong: Anthropic's
|
||||
* anti-abuse gate uses the suffix to fingerprint third-party clients
|
||||
* (hermes / OpenCode / Cline) and rate-limits them harder. Real Claude
|
||||
* Code (Electron + Node + official Anthropic JS SDK) emits the bare
|
||||
* {@code claude-cli/<v>} form, which is what openclaw
|
||||
* ({@code anthropic-transport-stream.ts:30,572}) also uses. Verified by
|
||||
* reproducing 429 with the suffix and {@code anthropic-ratelimit-*}
|
||||
* headers absent — the diagnostic signature of the anti-abuse path.
|
||||
* to the User-Agent. That turned out to be wrong: Anthropic's anti-abuse
|
||||
* gate uses the suffix to fingerprint third-party clients and rate-limits
|
||||
* them harder. Real Claude Code (Electron + Node + official Anthropic JS
|
||||
* SDK) emits the bare {@code claude-cli/<v>} form. Verified by reproducing
|
||||
* 429 with the suffix and {@code anthropic-ratelimit-*} headers absent —
|
||||
* the diagnostic signature of the anti-abuse path.
|
||||
*/
|
||||
public String userAgent() {
|
||||
return "claude-cli/" + versionDetector.get();
|
||||
|
||||
@ -36,9 +36,6 @@ import java.util.concurrent.TimeUnit;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Reference: hermes-agent {@code anthropic_adapter._read_claude_code_credentials_from_keychain}
|
||||
* (line 470) and {@code read_claude_code_credentials} (line 530).
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ -48,8 +45,8 @@ public class ClaudeCodeCredentialsReader {
|
||||
/** macOS Keychain service name written by Claude Code. */
|
||||
static final String KEYCHAIN_SERVICE_NAME = "Claude Code-credentials";
|
||||
|
||||
/** Hermes also queries {@code ~/.claude.json primaryApiKey} but that's a
|
||||
* managed key, not OAuth — intentionally not read here. */
|
||||
/** {@code ~/.claude.json primaryApiKey} is a managed key, not OAuth —
|
||||
* intentionally not read here. */
|
||||
static final Path JSON_CREDENTIALS_PATH =
|
||||
Paths.get(System.getProperty("user.home"), ".claude", ".credentials.json");
|
||||
|
||||
|
||||
@ -45,10 +45,6 @@ import java.util.concurrent.TimeUnit;
|
||||
* preserve {@code scopes} (Claude Code >= 2.1.81 requires
|
||||
* {@code user:inference}) and any future fields we don't know about.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Reference: hermes-agent
|
||||
* {@code anthropic_adapter._write_claude_code_credentials} (line 684-727)
|
||||
* and {@code _write_claude_code_credentials_to_keychain} (line 730+).
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
|
||||
@ -31,9 +31,6 @@ import java.util.Optional;
|
||||
* <p>This service does NOT handle the OAuth login flow itself — that is
|
||||
* RFC-062 PR-4. Until then, MateClaw piggybacks on whatever credentials the
|
||||
* user already has on disk from their installed Claude Code client.
|
||||
*
|
||||
* <p>Reference: hermes-agent {@code anthropic_adapter._get_claude_code_token}
|
||||
* + {@code _ensure_claude_code_token_fresh} (lines 540-605).
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
|
||||
@ -32,16 +32,13 @@ import java.util.Map;
|
||||
* <p>The refresh request is a vanilla OAuth 2.0 refresh-token grant with the
|
||||
* public Claude Code {@code client_id}. We must spoof the Claude Code
|
||||
* {@code User-Agent} — Anthropic's edge filters drop unrecognised UAs.
|
||||
*
|
||||
* <p>Reference: hermes-agent {@code anthropic_adapter._refresh_claude_code_token}
|
||||
* (line 605+).
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ClaudeCodeTokenRefresher {
|
||||
|
||||
/** Public Claude Code OAuth client_id. Same value hermes-agent and OpenCode use. */
|
||||
/** Public Claude Code OAuth client_id. */
|
||||
static final String CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
||||
|
||||
/** Endpoints tried in order until one succeeds. */
|
||||
|
||||
@ -22,9 +22,6 @@ import java.util.regex.Pattern;
|
||||
* where Claude Code isn't installed locally (the OAuth flow may still work via
|
||||
* a manually-imported credentials file).
|
||||
*
|
||||
* <p>Reference: hermes-agent {@code anthropic_adapter._detect_claude_code_version}
|
||||
* (line 239) + {@code _CLAUDE_CODE_VERSION_FALLBACK} (line 235).
|
||||
*
|
||||
* <p>Result is cached for the JVM lifetime (Anthropic's UA validation tolerates
|
||||
* a stable version per process). Restart MateClaw to pick up a Claude Code
|
||||
* upgrade.
|
||||
@ -36,7 +33,7 @@ public class ClaudeCodeVersionDetector {
|
||||
/**
|
||||
* Static fallback version. Update this when bumping the floor at which
|
||||
* Anthropic accepts spoofed Claude Code traffic (track Anthropic's
|
||||
* announcements + hermes-agent's same constant for cadence).
|
||||
* announcements for cadence).
|
||||
*/
|
||||
static final String FALLBACK_VERSION = "2.1.74";
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ import java.util.Map;
|
||||
*
|
||||
* <p>映射规则(对应 RFC-014 Change 1 的 SystemAndTailCacheStrategy 默认行为):
|
||||
* <ul>
|
||||
* <li>{@code includeToolsBlock=true} → {@link AnthropicCacheStrategy#CONVERSATION_HISTORY}(system + tools + 最新一段对话;最完整也最贴近 hermes-agent system_and_3)</li>
|
||||
* <li>{@code includeToolsBlock=true} → {@link AnthropicCacheStrategy#CONVERSATION_HISTORY}(system + tools + 最新一段对话;覆盖最完整)</li>
|
||||
* <li>{@code includeToolsBlock=false} → {@link AnthropicCacheStrategy#SYSTEM_ONLY}</li>
|
||||
* <li>{@code ttl=extended-1h} → SYSTEM/USER 两个消息类型映射为 {@link AnthropicCacheTtl#ONE_HOUR}</li>
|
||||
* <li>{@code minPromptTokens} → 转字符长度(× 4,粗粒度估算)作为 SYSTEM 段的 min content length</li>
|
||||
|
||||
@ -7,7 +7,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认策略:在 system / tools / messages 尾部最多打 4 个缓存断点(仿 hermes-agent {@code system_and_3})。
|
||||
* 默认策略:在 system / tools / messages 尾部最多打 4 个缓存断点。
|
||||
*
|
||||
* <p>断点选择规则(按优先级递减):
|
||||
* <ol>
|
||||
|
||||
@ -103,7 +103,7 @@ public class BuiltinSkillSeedService implements ApplicationRunner {
|
||||
// from the previous successful run AND the DB still holds the same
|
||||
// number of builtin rows, nothing on disk changed since last seed
|
||||
// and we can skip the parse / select / update loop entirely.
|
||||
// Hermes-style trick: stat-only check, no content read.
|
||||
// The check is stat-only — no content read.
|
||||
Map<String, long[]> currentManifest = buildResourceManifest(resources);
|
||||
SeedSnapshot snapshot = loadSnapshot();
|
||||
if (snapshot != null
|
||||
|
||||
@ -643,7 +643,7 @@ public class SkillService {
|
||||
return "";
|
||||
}
|
||||
|
||||
// --- 第零层:Skill 自治引导(RFC-023,对标 hermes-agent prompt_builder.py:164-171) ---
|
||||
// --- 第零层:Skill 自治引导 ---
|
||||
StringBuilder catalog = new StringBuilder();
|
||||
catalog.append("\n\n## Skill Management\n\n");
|
||||
catalog.append("After completing a complex task (5+ tool calls), fixing a tricky error, ");
|
||||
|
||||
@ -21,8 +21,8 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* RFC-023: Agent 自治 Skill 管理工具
|
||||
* <p>
|
||||
* 对标 hermes-agent 的 skill_manager_tool.py,让 Agent 在对话中自主创建、编辑、
|
||||
* 修补和删除 Skill。每次写入前强制安全扫描,失败则拒绝并返回原因。
|
||||
* 让 Agent 在对话中自主创建、编辑、修补和删除 Skill。
|
||||
* 每次写入前强制安全扫描,失败则拒绝并返回原因。
|
||||
* <p>
|
||||
* 系统 prompt 引导 Agent 使用此工具:
|
||||
* <blockquote>
|
||||
|
||||
@ -23,8 +23,7 @@ import java.util.Set;
|
||||
* 同步模式:返回图片 URL(DALL-E 系列)或 base64 data URL(gpt-image-2 系列)。
|
||||
* 复用已有的 OpenAI LLM provider 的 API Key。
|
||||
*
|
||||
* <p>gpt-image-2 三档质量做成 3 个虚拟 model ID(参考 hermes-agent
|
||||
* plugins/image_gen/openai/__init__.py 的 model catalog 设计),让 picker
|
||||
* <p>gpt-image-2 三档质量做成 3 个虚拟 model ID,让 picker
|
||||
* 能直接选 fast/balanced/high。三档底层都打到 API model {@code "gpt-image-2"},
|
||||
* 区别仅在 {@code quality} 参数。
|
||||
*
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body
|
||||
-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}.
|
||||
--
|
||||
-- Reference: hermes-agent anthropic_adapter.py _ANTHROPIC_OUTPUT_LIMITS
|
||||
-- (lines 65-93) lists claude-opus-4-7 but no claude-sonnet-4-7. The latest
|
||||
-- released Sonnet remains claude-sonnet-4-6 (released alongside Opus 4.6).
|
||||
-- Anthropic's current model line includes claude-opus-4-7 but no
|
||||
-- claude-sonnet-4-7. The latest released Sonnet remains claude-sonnet-4-6
|
||||
-- (released alongside Opus 4.6).
|
||||
--
|
||||
-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281
|
||||
-- so user-customised settings (default flag, enabled flag) survive.
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body
|
||||
-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}.
|
||||
--
|
||||
-- Reference: hermes-agent anthropic_adapter.py _ANTHROPIC_OUTPUT_LIMITS
|
||||
-- (lines 65-93) lists claude-opus-4-7 but no claude-sonnet-4-7. The latest
|
||||
-- released Sonnet remains claude-sonnet-4-6 (released alongside Opus 4.6).
|
||||
-- Anthropic's current model line includes claude-opus-4-7 but no
|
||||
-- claude-sonnet-4-7. The latest released Sonnet remains claude-sonnet-4-6
|
||||
-- (released alongside Opus 4.6).
|
||||
--
|
||||
-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281
|
||||
-- so user-customised settings (default flag, enabled flag) survive.
|
||||
|
||||
@ -16,8 +16,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
* AND {@code oauth-2025-04-20}, comma-joined (no spaces).</li>
|
||||
* <li>{@code User-Agent} must be the bare {@code claude-cli/<ver>} —
|
||||
* NOT {@code claude-cli/<ver> (external, cli)}. The {@code (external, cli)}
|
||||
* suffix is what hermes-agent and other third-party clients append, and
|
||||
* Anthropic uses it as a fingerprint to rate-limit the anti-abuse path.
|
||||
* suffix is what third-party clients append, and Anthropic uses it as a
|
||||
* fingerprint to rate-limit the anti-abuse path.
|
||||
* Real Claude Code emits the bare form via the official JS SDK.</li>
|
||||
* </ol>
|
||||
*/
|
||||
@ -36,7 +36,7 @@ class ClaudeCodeApiHeadersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("allBetas: common betas appear before OAuth-only betas (matches hermes-agent ordering)")
|
||||
@DisplayName("allBetas: common betas appear before OAuth-only betas")
|
||||
void allBetas_orderedCommonFirst() {
|
||||
String result = headers.allBetas();
|
||||
int oauthIdx = result.indexOf("oauth-2025-04-20");
|
||||
|
||||
@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
/**
|
||||
* Verifies the OAuth-mode prompt rewriting that prevents Anthropic's edge
|
||||
* from rate-limiting MateClaw traffic. Each test corresponds to one of the
|
||||
* transforms hermes-agent applies on {@code is_oauth=True} requests.
|
||||
* transforms applied on OAuth-authenticated requests.
|
||||
*/
|
||||
class ClaudeCodeIdentityChatModelDecoratorTest {
|
||||
|
||||
@ -56,8 +56,8 @@ class ClaudeCodeIdentityChatModelDecoratorTest {
|
||||
Prompt input = new Prompt(List.of(new UserMessage("hello")));
|
||||
Prompt result = d.transform(input);
|
||||
|
||||
// First message must be a system message with just the identity prefix —
|
||||
// hermes-agent does the same: system = [cc_block] when none was supplied.
|
||||
// First message must be a system message with just the identity prefix:
|
||||
// when none was supplied, system = [identity block].
|
||||
Message first = result.getInstructions().get(0);
|
||||
assertTrue(first instanceof SystemMessage);
|
||||
assertEquals(ClaudeCodeIdentityChatModelDecorator.CLAUDE_CODE_SYSTEM_PREFIX,
|
||||
@ -70,9 +70,8 @@ class ClaudeCodeIdentityChatModelDecoratorTest {
|
||||
@DisplayName("transform is idempotent — second pass doesn't double-prefix")
|
||||
void transform_idempotent() {
|
||||
// Defends against accidental double-wrapping (e.g. nested decorators or
|
||||
// a re-issue of the same Prompt). hermes-agent doesn't have this concern
|
||||
// because its rewrite happens in one place; we keep this guard so the
|
||||
// identity prefix doesn't compound to "You are Claude Code...You are Claude Code...".
|
||||
// a re-issue of the same Prompt). The guard keeps the identity prefix
|
||||
// from compounding to "You are Claude Code...You are Claude Code...".
|
||||
ClaudeCodeIdentityChatModelDecorator d = new ClaudeCodeIdentityChatModelDecorator(noopDelegate());
|
||||
Prompt original = new Prompt(List.of(new SystemMessage("Body"), new UserMessage("hi")));
|
||||
Prompt once = d.transform(original);
|
||||
|
||||
@ -11,8 +11,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
/**
|
||||
* Unit tests for {@link OpenAiImageProvider} GPT-Image-2 wiring.
|
||||
*
|
||||
* <p>Inspired by hermes-agent's plugins/image_gen/openai/__init__.py — three
|
||||
* virtual model IDs (gpt-image-2-low/medium/high) all map to API model
|
||||
* <p>Three virtual model IDs (gpt-image-2-low/medium/high) all map to API model
|
||||
* {@code gpt-image-2} with a different {@code quality} parameter. The new
|
||||
* size set is 1024x1024 / 1024x1536 / 1536x1024, distinct from DALL-E's
|
||||
* 1024x1024 / 1024x1792 / 1792x1024.
|
||||
@ -86,8 +85,7 @@ class OpenAiImageProviderGptImage2Test {
|
||||
assertEquals("medium", OpenAiImageProvider.qualityForTier("gpt-image-2-medium"));
|
||||
assertEquals("high", OpenAiImageProvider.qualityForTier("gpt-image-2-high"));
|
||||
|
||||
// Defensive: any unrecognised id falls back to medium (sane default;
|
||||
// matches hermes-agent DEFAULT_MODEL = gpt-image-2-medium).
|
||||
// Defensive: any unrecognised id falls back to medium (sane default).
|
||||
assertEquals("medium", OpenAiImageProvider.qualityForTier("anything-else"));
|
||||
assertEquals("medium", OpenAiImageProvider.qualityForTier(""));
|
||||
}
|
||||
@ -134,7 +132,7 @@ class OpenAiImageProviderGptImage2Test {
|
||||
@DisplayName("normalizeSize: extra gpt-image-2 aspect-ratio aliases (3:4, 2:3, 4:3, 3:2) work")
|
||||
void normalizeSize_gptImage2_extraAspectAliases() {
|
||||
OpenAiImageProvider p = newProvider();
|
||||
// Per hermes-agent's spec: portrait aliases → 1024x1536, landscape → 1536x1024
|
||||
// Portrait aliases → 1024x1536, landscape → 1536x1024
|
||||
assertEquals("1024x1536", p.normalizeSize(null, "3:4", true));
|
||||
assertEquals("1024x1536", p.normalizeSize(null, "2:3", true));
|
||||
assertEquals("1536x1024", p.normalizeSize(null, "4:3", true));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user