- Replaced push/release triggers with workflow_run on Release completion
- Added check-release job to verify a new release exists before building
- Simplified publish job to use tag from check-release output
- This solves the GITHUB_TOKEN limitation where events don't trigger
other workflows
These plugins push commits directly to main, which conflicts with
branch protection rules (PR required, signed commits). The tag and
GitHub Release are created via API by @semantic-release/github
and don't need push access.
- Switched from custom GH_TOKEN PAT to built-in GITHUB_TOKEN (always valid)
- Added push tags trigger (v*) to publish.yaml since GITHUB_TOKEN
releases don't fire the 'on: release' event
The DailyRotateFile transport in api-logger.ts creates directories at
module load time. When the paths mock returns '/mock/agent/dir', the
real file-stream-rotator fails with EACCES on CI Linux runners.
Added vi.mock for both api-logger and api-logging.plugin to prevent
the real DailyRotateFile from initializing during tests.
- Create .releaserc.json with conventional commits, changelog, npm version bump, and GitHub release
- Regenerate all build icons (PNG, ICO, ICNS) from new GN logo
- Normalize Node.js version to 20 across all CI workflows (testing, lint, format, release)
Backend fixes:
- Fix totalTokens defaulting to 0 when API omits totalTokenCount (now computes prompt+completion)
- Fix streaming metadata extraction: unwrap response wrapper in Anthropic/Gemini handlers
- Fix Zod schemas: handle SQL SUM(null) with nullable().transform()
- Add is_estimated column migration to token_usage table
Frontend redesign:
- Rewrite Usage page with area charts (nivo/line), horizontal bar model ranking
- StatCard: add trend indicators (% vs previous period) and inline SVG sparklines
- Premium color palette, prompt/completion ratio bar, top models mini-list
- Live indicator with 15s auto-refresh
Design system migration:
- Standardize all components on Gemini Nexus tokens (rounded-xl, border-white/[0.06])
- Refine UI components, proxy tabs, cloud account list, provider groups
- Emit tool call id+name in first SSE delta and arguments in subsequent deltas
- Split large tool arguments into 1000-char chunks to prevent framing issues
- Set FastifyAdapter bodyLimit to 10MB to handle large payloads
- Add TDD tests for tool call streaming verification
- Add cacheHitRate to ProxyMetricsService, handler, and router schema
- Fix StatCard to support string values and purple accent
- Fix duplicate tabs keys in i18n translations (EN/ZH/RU)
- Add missing cacheStatus translations across all languages
- Change author from Draculabo to evandrodevbr in package.json
- Update GitHub publisher config in forge.config.ts (owner/repo/iconUrl)
- Add favicon link in index.html pointing to src/assets/icon.png
- Ensure all app icon references use images/icon.* consistently
- Create scripts/build-windows.js that extracts wix314-binaries.zip to tools/wix314/
- Adds extracted dir to PATH so electron-wix-msi can find candle.exe/light.exe
- Adds npm script 'make:win' for one-command Windows installer build
The initial role chunk (role:'assistant', content:'') added in a3eabc2
was causing the OpenCode TUI to receive an empty first delta and
discard all subsequent content, showing only the completion time with
no text output.
Restored to the exact streaming behavior from e524a340 that worked:
- Remove the pre-emptive initial chunk entirely
- Move hasEmittedChunk=true back inside pushChunk (original position)
- Use reasoning_content delta field for thought parts instead of
wrapping in <think> tags (which corrupts plain text responses)
- Remove streamOptions/include_usage/lastUsageMetadata tracking
(not present in the working version)
- Remove isThinking state machine (was adding </think> noise to output)
Kept from the spec compliance work (a3eabc2):
- toolCallIndex counter for correct finish_reason:'tool_calls' mapping
- Comment explaining why we override Gemini's STOP finish reason
Route fixes:
- Add POST /v1/messages/chat/completions alias so OpenCode clients
misconfigured with baseURL ending in /v1/messages no longer get 404
Session affinity:
- Extract x-session-affinity HTTP header (sent by OpenCode) and use it
as the session key for token stickiness, falling back to body-level
session_id fields if the header is absent
Tool calling (streaming):
- finish_reason is now 'tool_calls' when function calls were emitted in
the stream (Gemini always sends STOP, so we check toolCallIndex > 0)
- createSyntheticOpenAIStream (stream fallback path) now emits all
tool_call deltas before the finish chunk instead of silently dropping
them when content is null
Tool calling (non-streaming):
- content is now always null when tool_calls are present, satisfying
strict clients that reject non-null content alongside tool_calls
- system_fingerprint field added to OpenAIChatResponse interface and
response builder for OpenAI spec compliance
Usage accounting:
- thoughtsTokenCount is now included in completion_tokens and
total_tokens for the streaming usage chunk, fixing undercount for
thinking/reasoning model runs