feat(config): add experimental config for gating unstable features (#110)

* feat(anthropic-auto-compact): add aggressive truncation and empty message recovery

Add truncateUntilTargetTokens method, empty content recovery mechanism, and
emptyContentAttemptBySession tracking for robust message handling.

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

* feat(session-recovery): add auto-resume and recovery callbacks

Implement ResumeConfig, resumeSession() method, and callback support for
enhanced session recovery and resume functionality.

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

* feat(config): add experimental config schema for gating unstable features

This adds a new 'experimental' config field to the OhMyOpenCode schema that enables fine-grained control over unstable/experimental features:

- aggressive_truncation: Enables aggressive token truncation in anthropic-auto-compact hook for more aggressive token limit handling
- empty_message_recovery: Enables empty message recovery mechanism in anthropic-auto-compact hook for fixing truncation-induced empty message errors
- auto_resume: Enables automatic session resume after recovery in session-recovery hook for seamless recovery experience

The experimental config is optional and all experimental features are disabled by default, ensuring backward compatibility while allowing early adopters to opt-in to cutting-edge features.

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2025-12-19 02:45:59 +09:00
committed by GitHub
parent 69b1529c32
commit 164a7f8209
11 changed files with 422 additions and 33 deletions
@@ -25,6 +25,7 @@ const TOKEN_LIMIT_KEYWORDS = [
"token limit",
"context length",
"too many tokens",
"non-empty content",
]
function extractTokensFromMessage(message: string): { current: number; max: number } | null {
@@ -46,6 +47,13 @@ function isTokenLimitError(text: string): boolean {
export function parseAnthropicTokenLimitError(err: unknown): ParsedTokenLimitError | null {
if (typeof err === "string") {
if (err.toLowerCase().includes("non-empty content")) {
return {
currentTokens: 0,
maxTokens: 0,
errorType: "non-empty content",
}
}
if (isTokenLimitError(err)) {
const tokens = extractTokensFromMessage(err)
return {
@@ -142,6 +150,14 @@ export function parseAnthropicTokenLimitError(err: unknown): ParsedTokenLimitErr
}
}
if (combinedText.toLowerCase().includes("non-empty content")) {
return {
currentTokens: 0,
maxTokens: 0,
errorType: "non-empty content",
}
}
if (isTokenLimitError(combinedText)) {
return {
currentTokens: 0,