Skip to content

proper fix for image attachments#26

Closed
shuv1337 wants to merge 18 commits intodevfrom
fix-tui-img-attachments
Closed

proper fix for image attachments#26
shuv1337 wants to merge 18 commits intodevfrom
fix-tui-img-attachments

Conversation

@shuv1337
Copy link
Collaborator

@shuv1337 shuv1337 commented Nov 21, 2025

When reading text files via ReadTool, append any returned attachments (e.g., images) as synthetic file parts; otherwise fall back to the original file part.

  • Session handling (packages/opencode/src/session/prompt.ts):
    • ReadTool file handling:
      • After executing ReadTool for text/plain file inputs, push the text output and then:
        • If result.attachments exist, append them as synthetic file parts with filename defaulted to the original part’s filename.
        • Otherwise, append the original file part.
      • Ensures attachments (e.g., images) from tools are correctly attached to the message.
image image image

Note

When reading text files via ReadTool, always add the text output and, if present, append returned attachments as synthetic file parts; otherwise, keep the original file part.

  • Session handling (packages/opencode/src/session/prompt.ts)
    • ReadTool (text/plain) handling:
      • Push result.output as a synthetic text part.
      • If result.attachments exist, append them as synthetic file parts with filename defaulting to the original part.filename and proper messageID/sessionID.
      • Otherwise, append the original file part.

Written by Cursor Bugbot for commit 65b7569. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Toggleable message timestamps with persisted setting; messages show concise or full datetime and assistant messages display duration badges when available.
  • Refactor

    • Tool outputs now include structured attachment entries only when attachments are present (no extra parts otherwise).
  • Chores

    • Updated build hash and migrated app tooling/configs; multiple packages bumped to v1.0.103.

✏️ Tip: You can customize this high-level summary in your review settings.

messageID: info.id,
sessionID: input.sessionID,
})),
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: FilePart schema lacks synthetic field support

The code attempts to set synthetic: true on FilePart objects created from ReadTool attachments (line 911), but the FilePart Zod schema in message-v2.ts does not define a synthetic property. This property is only defined for TextPart. The synthetic metadata will be stripped when the part is validated or persisted, defeating the intended behavior of marking attachments as synthetic.

Fix in Cursor Fix in Web

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Multiple packages updated: attachment handling in the Read tool was made conditional, provider/model option merging standardized via deep-merge pipelines, several packages migrated configs from Vinxi to Vite/Nitro, new timestamp toggle and locale/time helpers added, compaction/summarization prompt changes introduced, and many package version bumps and minor metadata edits.

Changes

Cohort / File(s) Summary
Read tool attachment handling
packages/opencode/src/session/prompt.ts
Unconditionally push the tool text part; if attachments exist, push per-attachment synthetic, normalized entries with IDs; if no attachments, push the original file part instead.
Provider/model options & small-model title flow
packages/opencode/src/provider/transform.ts, packages/opencode/src/session/prompt.ts, packages/opencode/src/session/summary.ts, packages/opencode/src/session/compaction.ts
Replaced ad-hoc spreads with pipe/mergeDeep composition for provider, small-model, and model/info options; added exported smallOptions; adjusted ensureTitle and chat-params option construction; compaction uses merged providerOptions. (Note: smallOptions appears duplicated in the file.)
Model selection fallback
packages/opencode/src/provider/provider.ts
Guard model selection by provider existence and add explicit fallback to opencode/gpt-5-nano when no matching model is found.
Prompt and system prompt changes
packages/opencode/src/session/system.ts, packages/opencode/src/session/prompt/compaction.txt, packages/opencode/src/session/prompt/summarize.txt, packages/opencode/src/session/prompt/title.txt, packages/opencode/src/session/prompt/summarize-turn.txt
Added new compaction prompt wiring and compaction() SystemPrompt function; added compaction.txt; simplified summarize prompts and removed summarize-turn.txt; updated title rules (disallow "summarizing").
Summarization behavior
packages/opencode/src/session/summary.ts
Use merged providerOptions for title and summary calls; prune tool outputs by replacing them with "[TOOL OUTPUT PRUNED]" where applicable.
Compaction prompt usage
packages/opencode/src/session/compaction.ts
Switched system prompt to SystemPrompt.compaction and merged provider/model/info options via mergeDeep pipeline.
Locale/time utilities
packages/opencode/src/util/locale.ts
Added/updated time formatting functions: time, datetime, todayTimeOrDateTime, and duration (explicit string returns and new helpers).
TUI session — timestamps & UI changes
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Added showTimestamps flag to Session context with KV persistence and UI toggle; adjusted message header rendering and duration badge logic; UserMessage/AssistantMessage use new context and locale helpers.
Middleware / server API typings
packages/console/app/src/middleware.ts, packages/console/app/src/context/auth.session.ts, packages/console/app/src/global.d.ts, packages/enterprise/src/global.d.ts
Switched middleware import/use to createMiddleware from @solidjs/start/middleware; updated useSession import; added ambient module declaring APIEvent for @solidjs/start/server.
Vinxi → Vite / Nitro migration (configs & scripts)
packages/console/app/app.config.ts (removed), packages/console/app/vite.config.ts (new), packages/console/app/package.json, packages/enterprise/app.config.ts (removed), packages/enterprise/vite.config.ts (new), packages/enterprise/package.json, packages/desktop/vite.config.ts, packages/console/app/tsconfig.json, packages/enterprise/tsconfig.json
Removed old Vinxi/SolidStart config files and added Vite configs; updated package scripts and dependencies to use Vite/Nitro (added nitro, vite-plugin, replaced vinxi); tsconfig types switched to vite/client.
Frontend route/header removals
packages/console/app/src/routes/index.tsx, packages/console/app/src/routes/zen/index.tsx
Removed import/use of HttpHeader and its JSX usage (commented out), disabling the previous header rendering.
Type / config and minor code edits
packages/desktop/vite.config.ts, packages/console/app/.gitignore, packages/enterprise/.gitignore, packages/ui/.gitignore
Casting plugins array to any in desktop Vite config; removed .vinxi and related ignore patterns from several .gitignore files.
Version bumps & metadata only
multiple package.json files (e.g., packages/opencode/package.json, packages/console/*, packages/desktop/package.json, packages/ui/package.json, packages/plugin/package.json, packages/function/package.json, packages/tauri/package.json, packages/sdk/js/package.json, sdks/vscode/package.json, packages/enterprise/*, packages/extensions/zed/extension.toml, nix/hashes.json, STATS.md, package.json)
Patch/version bumps across many packages; updated nix/hashes.json nodeModules hash; one dependency replaced with a remote URL in top-level package.json workspaces.catalog for @solidjs/start.
Duplicate/formatting concerns
packages/opencode/src/provider/transform.ts
smallOptions is declared twice (duplicate export), and options return type tightened to always return an object.

Sequence Diagram(s)

sequenceDiagram
    participant Tool as Read Tool
    participant Prompt as Prompt Builder
    participant Parts as Parts Array

    Note over Tool,Prompt: Tool returns { text, part, attachments? }

    Tool->>Prompt: return { text, part, attachments? }
    Prompt->>Parts: push(text_part)  %% always
    alt attachments exist (non-empty)
        loop attachment
            Prompt->>Parts: push(attachment_entry { synthetic, filename_normalized, ids })
        end
    else attachments absent or empty
        Prompt->>Parts: push(original_file_part)
    end
Loading
sequenceDiagram
    participant Caller as Title/Summary Flow
    participant Compose as Options Composer (pipe + mergeDeep)
    participant Provider as ProviderTransform.options
    participant ModelInfo as model.info.options

    Caller->>Compose: request merged options (providerID, modelID, session)
    Compose->>Provider: include ProviderTransform.options
    Compose->>ModelInfo: include smallOptions / model info
    Compose->>Caller: return merged options
    Caller->>Provider: call provider with merged options for title/summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • packages/opencode/src/provider/transform.ts — duplicate smallOptions export and changed options signature/return type.
    • Option composition changes: verify mergeDeep semantics don't unintentionally override important keys.
    • Read tool attachment conditional: ensure empty arrays vs undefined are handled consistently and existing consumers still receive expected parts shape.
    • Migration from Vinxi to Vite/Nitro: check build scripts, server compat flags, and cloudflare-specific conditional build script.
    • TUI timestamp persistence: KV usage and context typing changes.
    • Search for any remaining references to removed config files (app.config.ts) or Vinxi imports.

Poem

🐇 I stitched the text, then peered inside the pack,
If tiny files arrived, I sewed them back.
Options merged like leaves in spring,
Configs hopped over to a Vite-born ring.
A tidy burrow — small changes, big crack. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.85% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'proper fix for image attachments' clearly describes the main change: improving how image attachments are handled in the ReadTool integration, which aligns with the primary objective of preserving attachments returned by ReadTool.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-tui-img-attachments

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d36f84b and 67830f7.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • package.json (1 hunks)
  • packages/console/app/package.json (1 hunks)
  • packages/console/core/package.json (1 hunks)
  • packages/console/function/package.json (1 hunks)
  • packages/console/mail/package.json (1 hunks)
  • packages/desktop/package.json (1 hunks)
  • packages/extensions/zed/extension.toml (2 hunks)
  • packages/function/package.json (1 hunks)
  • packages/opencode/package.json (1 hunks)
  • packages/opencode/src/tool/bash.ts (1 hunks)
  • packages/plugin/package.json (1 hunks)
  • packages/sdk/js/package.json (1 hunks)
  • packages/slack/package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/util/package.json (1 hunks)
  • packages/web/package.json (1 hunks)
  • packages/web/src/content/docs/zen.mdx (1 hunks)
  • sdks/vscode/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (7)
  • packages/slack/package.json
  • sdks/vscode/package.json
  • packages/sdk/js/package.json
  • packages/desktop/package.json
  • packages/ui/package.json
  • packages/console/mail/package.json
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: update
  • GitHub Check: update
🔇 Additional comments (10)
packages/function/package.json (1)

3-3: Verify version bump is coordinated across the monorepo.

The version is being bumped from 1.0.85 to 1.0.86 as part of a broader PR-wide update affecting multiple packages. Confirm that this versioning strategy is intentional and that all interdependent packages are being updated consistently, especially since the PR objectives focus on changes in packages/opencode/src/session/prompt.ts rather than this utility package.

packages/plugin/package.json (1)

4-4: Version bump looks consistent with the rest of the release.

Manifest only changes the package version to 1.0.86; scripts/dependencies are unchanged, so no behavioral impact here.

packages/web/package.json (1)

4-4: Web package version bump is straightforward.

Only the version is updated to 1.0.86; no script or dependency changes, so runtime behavior is unchanged.

packages/console/function/package.json (1)

3-3: Console function package version bump is fine.

Version moves to 1.0.86 with no other manifest changes; this is a safe, coordinated release bump.

packages/console/core/package.json (1)

4-4: Console core manifest update is minimal and safe.

Just bumps the version to 1.0.86; dependency and script surfaces remain identical.

packages/util/package.json (1)

3-3: Util package version bump is OK.

Version is updated to 1.0.86 with no other manifest differences, so there’s no runtime change.

packages/opencode/package.json (1)

3-3: Core opencode package version bump is coherent with the rest of the repo.

Version is now 1.0.86; bin/export, scripts, and dependency graph are unchanged, so behavior is preserved while marking the new release.

packages/opencode/src/tool/bash.ts (1)

185-187: Using $SHELL for spawn improves UX while preserving the old fallback.

Switching from shell: true to shell: process.env.SHELL ?? true means:

  • When SHELL is set (common on macOS/Linux, sometimes on Windows with Git Bash), commands now run under the user’s configured shell, which is usually what they expect.
  • When SHELL is unset, behavior falls back to the previous default (shell: true), so no change in those environments.

The rest of the process management (cwd, env, detached, timeout/killTree handling) is unchanged, so this is a targeted, low‑risk improvement. The main edge case to be aware of is a misconfigured SHELL pointing to a non‑existent executable, which would now cause spawn to error where it previously worked; that should surface clearly via the existing "error" listener and is an acceptable trade‑off.

If you want to double‑check cross‑platform behavior, especially on Windows and in Git Bash/WSL setups, you could run a quick manual smoke test executing a few simple commands (echo, pwd, ls/dir) under typical shells to confirm everything behaves as expected.

packages/extensions/zed/extension.toml (1)

4-4: All referenced v1.0.86 assets are reachable; no further action required.

The verification confirms that all five platform-specific archives (darwin-arm64, darwin-x64, linux-arm64, linux-x64, windows-x64) return HTTP 302 redirects from the GitHub release, indicating they exist and are properly accessible. The extension version and all archive URLs are internally consistent and point to valid, reachable assets.

packages/console/app/package.json (1)

10-10: Version bump aligned with coordinated release.

The version change from 1.0.85 to 1.0.86 is consistent with the PR objectives and is part of the broader version bump across the repository.

Comment on lines 136 to 139
| GPT 5.1 | $1.07 | $8.50 | $0.107 | - |
| GPT 5.1 Codex | $1.07 | $8.50 | $0.107 | - |
| GPT 5 | $1.07 | $8.50 | $0.107 | - |
| GPT 5 Codex | $1.07 | $8.50 | $0.107 | - |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify pricing accuracy before release.

The pricing for GPT 5 family models has been updated to $1.07 input, $8.50 output, and $0.107 cached read per 1M tokens. Please confirm these rates reflect current OpenAI pricing to ensure the documentation remains accurate and customers receive correct billing information.


🌐 Web query:

What is the current pricing for GPT-5 models from OpenAI as of November 2025?

💡 Result:

As of November 21, 2025, OpenAI’s listed GPT‑5 API prices (per 1M tokens) are:

  • gpt-5 (standard): $1.25 input / $10.00 output. [1][2]
  • gpt-5-mini: $0.25 input / $2.00 output. [1][2]
  • gpt-5-nano: $0.05 input / $0.40 output. [1][2]
  • gpt-5-pro (higher‑performance tier): $15.00 input / $120.00 output. [1]

Sources: OpenAI pricing and GPT‑5 model docs. [1] [2]


🌐 Web query:

OpenAI GPT-5 pricing per 1M tokens

💡 Result:

As of Nov 21, 2025 (OpenAI Platform docs), GPT‑5 pricing per 1M tokens is:

  • gpt-5 / gpt-5-chat-latest: input $1.25, cached input $0.125, output $10.00 per 1M tokens. [1]

Source: OpenAI API pricing page. [1]


Update pricing to match current OpenAI rates.

The documentation contains inaccurate pricing for GPT-5 models. As of November 21, 2025, OpenAI's gpt-5 (standard) pricing is $1.25 input, $10.00 output, and $0.125 cached input per 1M tokens, not $1.07 / $8.50 / $0.107 as currently documented. Update lines 136-139 with the correct rates.

🤖 Prompt for AI Agents
packages/web/src/content/docs/zen.mdx lines 136-139: the pricing rows for GPT-5
variants are out of date; update the table cells for GPT 5.1, GPT 5.1 Codex, GPT
5, and GPT 5 Codex to reflect OpenAI's current rates by replacing the price
columns with $1.25 (input) | $10.00 (output) | $0.125 (cached input) per 1M
tokens so each of the four rows shows those three values instead of $1.07 /
$8.50 / $0.107.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/console/app/package.json (1)

13-28: Based on the context (package.json with frontend/fullstack tools), this likely refers to the nitro npm package. Let me search for the latest stable version:

Let me search for the specific Nitro v2 version details:

Alpha version of Nitro will introduce instability.

Line 25 uses nitro: 3.0.1-alpha.1, which is a pre-release version. For the current stable release, see Nitro v2.

Consider either:

  1. Using a stable v2 release instead
  2. Documenting why the alpha version is necessary
  3. Adding additional testing to validate stability
♻️ Duplicate comments (1)
packages/enterprise/package.json (1)

22-22: Alpha version of Nitro may introduce instability.

Same concern as in the console app: using nitro: 3.0.1-alpha.1 (pre-release) across multiple packages increases the risk of instability.

This issue affects both console and enterprise packages. Consider using a stable Nitro release if available, or document the rationale for requiring the alpha version.

🧹 Nitpick comments (11)
packages/enterprise/src/global.d.ts (1)

3-5: Consider consolidating duplicate type declarations.

The APIEvent type declaration for @solidjs/start/server is duplicated in both packages/enterprise/src/global.d.ts and packages/console/app/src/global.d.ts. While ambient declarations don't cause runtime conflicts, maintaining identical declarations in multiple locations can lead to inconsistencies if the type needs to evolve.

Consider:

  • Moving the shared type to a common type definitions package
  • Or documenting why each package needs its own declaration
packages/console/app/src/global.d.ts (1)

3-5: Consider consolidating duplicate type declarations.

This ambient module declaration for "@solidjs/start/server" is duplicated in packages/enterprise/src/global.d.ts with the identical APIEvent type. Maintaining duplicate type declarations can lead to inconsistencies if one is updated without the other.

Consider moving this shared type declaration to a common types package or ensuring they're imported from a single source.

packages/desktop/vite.config.ts (1)

12-12: Avoid as any type assertion for plugins.

The as any assertion bypasses TypeScript's type checking and is a code smell. This could hide legitimate type errors and makes the code harder to maintain.

If there's a type incompatibility between the plugin return types and Vite's expected PluginOption[], consider:

  1. Importing the correct types: import type { PluginOption } from 'vite'
  2. Using proper type assertions: plugins: [tailwindcss(), solidPlugin()] as PluginOption[]
  3. Investigating if the plugins need updates to match Vite's type expectations
-  plugins: [tailwindcss(), solidPlugin()] as any,
+  plugins: [tailwindcss(), solidPlugin()],

If the type error persists, please share the specific type error so we can address it properly rather than suppressing it.

packages/console/app/vite.config.ts (1)

7-7: Type cast suggests potential type mismatch.

The solidStart() plugin is explicitly cast to PluginOption, which may indicate a type incompatibility between the plugin and Vite's expected type.

Verify if this cast is necessary with the current versions. If the types don't align naturally, this could signal a version mismatch between @solidjs/start and vite.

packages/enterprise/vite.config.ts (1)

21-21: Type cast suggests potential type mismatch.

Similar to the console app, solidStart() is cast to PluginOption. This pattern appears across multiple configs, suggesting a systematic type incompatibility.

Verify if this cast is necessary with current package versions. Consider addressing the underlying type mismatch if possible.

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (2)

82-83: Consider using kv.signal for showTimestamps so persisted state actually rehydrates

The showTimestamps flag is wired correctly through context and the command palette, but it’s initialized via:

const [showTimestamps, setShowTimestamps] = createSignal(kv.get("timestamps", "hide") === "show")

Because kv.json() loads asynchronously, this kv.get runs before the KV store is hydrated, so the initial value will always default to "hide" in a fresh process, even if "timestamps": "show" is persisted on disk.

If you want the setting to reliably rehydrate, consider using the KV helper’s own reactive signal instead (and persisting a boolean):

const [showTimestamps, setShowTimestamps] = kv.signal<boolean>("timestamps", false)

// toggle:
setShowTimestamps((prev) => !prev)

That way the value tracks KV updates and reflects what’s on disk. (You may want to align sidebar with the same pattern in a follow-up.)

Also applies to: 113-114, 408-420, 722-731


999-1007: Assistant duration badge logic is sound; optionally avoid “0ms” when parent user isn’t found

The duration header correctly:

  • Pulls the sibling user message by parentID.
  • Computes completed - user.time.created and feeds it into Locale.duration.
  • Shows the badge only when time.completed is set and the step-finish reason isn’t tool-calls/unknown, or for the last message.

One small polish: when no matching user message is found, duration() returns 0, so a completed assistant message could render as ⬝0ms. If that’s undesirable, you could return undefined in that case and gate the <Show> on duration() > 0 instead.

Functionally this is fine as-is; the suggestion is just to avoid potentially confusing 0ms labels in rare edge cases.

Also applies to: 1044-1057

packages/opencode/src/provider/provider.ts (1)

660-684: Small-model fallback now hard-codes opencode/gpt-5-nano – verify assumptions

The new logic to:

  • Try a provider-specific priority list when provider exists, and
  • Unconditionally fall back to getModel("opencode", "gpt-5-nano") otherwise,

is clear and will give you a reliable small model in default setups. The only caveat is that this now couples getSmallModel to the presence of the opencode provider and the gpt-5-nano entry:

  • If a user configuration ever disables opencode or removes gpt-5-nano, getSmallModel will now throw from the fallback path, even if another provider has small models available.

Consider either:

  • Guarding the fallback with a check that opencode/gpt-5-nano actually exists before calling getModel, or
  • As a softer option, falling back to “first available small-ish model from any provider” when opencode isn’t present.

Not urgent, but worth aligning with how aggressively you want to depend on that specific default.

packages/opencode/src/session/compaction.ts (1)

18-18: Compaction prompt + provider options merge look good; consider guarding model.info.options

The changes here align compaction with the other flows:

  • SystemPrompt.compaction(model.providerID) reuses the same provider-specific spoofing pattern as summarize/title.
  • The deep-merge pipeline for providerOptions ({}ProviderTransform.options(...)model.info.options) is consistent with the new summary/prompt paths.

One minor robustness suggestion: if there’s any chance model.info.options can be undefined for some models, it would be safer to merge model.info.options ?? {} to avoid relying on mergeDeep’s behavior with undefined. Same note applies to the similar merge in summary.ts.

This isn’t blocking, just a small defensive tweak.

Also applies to: 100-101, 141-149

packages/opencode/src/session/summary.ts (1)

124-130: Tool-output pruning mutates in-memory messages; safe today, but cloning would future-proof

The loop that rewrites completed tool parts’ state.output to "[TOOL OUTPUT PRUNED]" happens on the in-memory messages array and is only used to build the summarization prompt. Since you don’t persist those mutated parts, existing behavior is correct and avoids sending large tool payloads to the small model.

If this function ever starts reusing the same messages instances for anything else after summarization, consider shallow-cloning the messages (or just the tool parts) prior to mutation to avoid surprising other consumers.

Not required now, but worth keeping in mind as this code evolves.

Also applies to: 145-146

packages/opencode/src/provider/transform.ts (1)

173-190: smallOptions is a sensible small-model preset; verify option names align with provider APIs

The new smallOptions({ providerID, modelID }) helper is a nice way to encode:

  • Lower reasoningEffort for OpenAI/gpt‑5 small models, and
  • Zero thinkingBudget for Google small models,

without hard-coding this logic at every call site.

Since these keys are provider-specific, worth double-checking that:

  • The reasoningEffort values ("low", "minimal") are valid for all gpt‑5‑family small models you intend to target.
  • The thinkingConfig.thinkingBudget shape matches what the Google-compatible models expect.

If any provider rejects unknown fields, you might want to gate these more tightly by exact model IDs rather than broad includes("gpt-5") / providerID === "google".

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 426a8cb and 0350785.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (49)
  • .github/workflows/deploy.yml (1 hunks)
  • STATS.md (1 hunks)
  • nix/hashes.json (1 hunks)
  • package.json (1 hunks)
  • packages/console/app/.gitignore (0 hunks)
  • packages/console/app/app.config.ts (0 hunks)
  • packages/console/app/package.json (2 hunks)
  • packages/console/app/src/context/auth.session.ts (1 hunks)
  • packages/console/app/src/global.d.ts (1 hunks)
  • packages/console/app/src/middleware.ts (1 hunks)
  • packages/console/app/src/routes/index.tsx (2 hunks)
  • packages/console/app/src/routes/zen/index.tsx (2 hunks)
  • packages/console/app/tsconfig.json (1 hunks)
  • packages/console/app/vite.config.ts (1 hunks)
  • packages/console/core/package.json (1 hunks)
  • packages/console/function/package.json (1 hunks)
  • packages/console/mail/package.json (1 hunks)
  • packages/desktop/package.json (1 hunks)
  • packages/desktop/vite.config.ts (1 hunks)
  • packages/enterprise/.gitignore (0 hunks)
  • packages/enterprise/app.config.ts (0 hunks)
  • packages/enterprise/package.json (2 hunks)
  • packages/enterprise/src/global.d.ts (1 hunks)
  • packages/enterprise/tsconfig.json (1 hunks)
  • packages/enterprise/vite.config.ts (1 hunks)
  • packages/extensions/zed/extension.toml (2 hunks)
  • packages/function/package.json (1 hunks)
  • packages/opencode/package.json (1 hunks)
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (8 hunks)
  • packages/opencode/src/provider/provider.ts (1 hunks)
  • packages/opencode/src/provider/transform.ts (2 hunks)
  • packages/opencode/src/session/compaction.ts (3 hunks)
  • packages/opencode/src/session/prompt.ts (3 hunks)
  • packages/opencode/src/session/prompt/compaction.txt (1 hunks)
  • packages/opencode/src/session/prompt/summarize-turn.txt (0 hunks)
  • packages/opencode/src/session/prompt/summarize.txt (1 hunks)
  • packages/opencode/src/session/prompt/title.txt (1 hunks)
  • packages/opencode/src/session/summary.ts (3 hunks)
  • packages/opencode/src/session/system.ts (2 hunks)
  • packages/opencode/src/util/locale.ts (2 hunks)
  • packages/plugin/package.json (1 hunks)
  • packages/sdk/js/package.json (1 hunks)
  • packages/slack/package.json (1 hunks)
  • packages/tauri/package.json (1 hunks)
  • packages/ui/.gitignore (0 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/util/package.json (1 hunks)
  • packages/web/package.json (1 hunks)
  • sdks/vscode/package.json (1 hunks)
💤 Files with no reviewable changes (6)
  • packages/console/app/.gitignore
  • packages/enterprise/app.config.ts
  • packages/ui/.gitignore
  • packages/enterprise/.gitignore
  • packages/opencode/src/session/prompt/summarize-turn.txt
  • packages/console/app/app.config.ts
✅ Files skipped from review due to trivial changes (9)
  • packages/util/package.json
  • packages/ui/package.json
  • packages/opencode/package.json
  • packages/slack/package.json
  • packages/console/function/package.json
  • packages/desktop/package.json
  • packages/opencode/src/session/prompt/compaction.txt
  • packages/console/core/package.json
  • packages/opencode/src/session/prompt/summarize.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • nix/hashes.json
  • packages/opencode/src/session/prompt.ts
🧰 Additional context used
🧬 Code graph analysis (5)
packages/enterprise/src/global.d.ts (1)
packages/console/app/src/global.d.ts (1)
  • APIEvent (4-4)
packages/opencode/src/session/summary.ts (3)
packages/opencode/src/provider/transform.ts (1)
  • options (131-171)
packages/opencode/src/session/message-v2.ts (2)
  • TextPart (57-71)
  • TextPart (72-72)
packages/opencode/src/session/index.ts (1)
  • messages (287-301)
packages/console/app/src/global.d.ts (1)
packages/enterprise/src/global.d.ts (1)
  • APIEvent (4-4)
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (3)
packages/opencode/src/cli/cmd/tui/context/sync.tsx (2)
  • use (26-347)
  • sync (316-341)
packages/opencode/src/cli/cmd/tui/context/kv.tsx (1)
  • use (7-49)
packages/opencode/src/util/locale.ts (1)
  • duration (40-60)
packages/opencode/src/provider/provider.ts (1)
packages/opencode/src/session/system.ts (1)
  • provider (27-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: update
🔇 Additional comments (26)
packages/opencode/src/session/prompt/title.txt (1)

15-15: LGTM.

The new rule prevents a common LLM failure mode where the system describes its own operation ("summarizing") rather than extracting the user's actual intent. This complements the existing directive on line 14 and aligns with the prompt's goal of extracting actionable titles directly from user messages.

packages/console/app/src/routes/index.tsx (1)

3-3: Same caching removal as zen route.

The HttpHeader component has been commented out here as well, matching the pattern in packages/console/app/src/routes/zen/index.tsx. Since this is the main landing page (likely higher traffic), the caching impact may be more significant.

The same questions from the zen route apply here. Additionally, verify whether this change is coordinated with any CDN or infrastructure configuration updates that might provide caching at a different layer.

Also applies to: 45-45

packages/web/package.json (1)

4-4: LGTM! Version bump is consistent with the release.

The version increment to 1.0.103 aligns with the PR-wide version bump across multiple packages.

packages/sdk/js/package.json (1)

4-4: LGTM! SDK version bump is consistent.

The version increment to 1.0.103 is consistent with the coordinated release across multiple packages.

packages/console/mail/package.json (1)

3-3: LGTM! Version bump looks good.

The version increment to 1.0.103 is consistent with the coordinated release.

packages/tauri/package.json (1)

4-4: LGTM! Version bump is consistent.

The version increment to 1.0.103 aligns with the coordinated release across packages.

packages/extensions/zed/extension.toml (1)

4-4: Release v1.0.103 artifacts verified as published and accessible.

The GitHub release v1.0.103 exists and contains all five required platform archives:

  • ✓ darwin-arm64
  • ✓ darwin-x64
  • ✓ linux-arm64
  • ✓ linux-x64
  • ✓ windows-x64

Additional baseline and musl variants are also available. The code change is verified as safe to merge.

packages/console/app/src/context/auth.session.ts (1)

1-1: Import migration is valid with confirmed API compatibility.

Verification confirms the migration from vinxi/http to @solidjs/start/http maintains full API compatibility:

  • useSession is the vinxi/http session helper that SolidStart expects and is supported in @solidjs/start server contexts
  • @solidjs/start is properly declared as a dependency in package.json
  • vinxi has been completely removed from dependencies (migration is complete)
  • The useSession<AuthSession>(...) usage in auth.session.ts follows the correct API pattern with no breaking changes
.github/workflows/deploy.yml (1)

20-22: Verify that dependencies and SST version support Node.js 24 via testing.

Node.js 24 is confirmed as Active LTS (supported through April 30, 2028), and SST (Serverless Stack) requires Node.js >= 18, making the addition justified. Bun and Node.js can coexist as separate runtime environments, so there is no direct conflict between the existing setup-bun step and the new setup-node setup.

However, while the change is sound in principle, you should manually verify that:

  • Your specific SST version (v2/v3) is fully compatible with Node.js 24
  • All transitive dependencies work correctly with Node.js 24
  • The application runs successfully in the deployment pipeline with this configuration

Consider adding the Node.js version constraint to package.json engines field for consistency: "engines": { "node": ">=24" } or your minimum compatible version.

packages/console/app/tsconfig.json (1)

15-15: LGTM! Migration from Vinxi to Vite types.

The update from vinxi/types/client to vite/client aligns with the broader migration to Vite/SolidStart tooling mentioned in the PR context.

packages/plugin/package.json (1)

4-4: LGTM! Standard version bump.

Version increment aligns with the repo-wide update to 1.0.103 across multiple packages.

STATS.md (1)

150-150: LGTM! Download statistics updated.

The new row tracks download metrics for 2025-11-22, with growth patterns consistent with historical data.

packages/function/package.json (1)

3-3: LGTM! Standard version bump.

Version increment aligns with the repo-wide update to 1.0.103.

sdks/vscode/package.json (1)

5-5: LGTM! Standard version bump.

Version increment aligns with the repo-wide update to 1.0.103.

package.json (1)

51-51: Verify the use of pkg.pr.new URL for @solidjs/start.

The dependency is pointing to a PR preview URL (https://pkg.pr.new/@solidjs/start@dbff19d) instead of a stable npm version. This has several implications:

  • PR preview deployments are temporary and may be removed
  • This is not suitable for production use
  • The specific commit (dbff19d) is pinned but not published to npm

If this is temporary for testing purposes, consider:

  1. Adding a TODO comment to track reverting to a stable version
  2. Documenting in the PR description why this preview is needed
  3. Planning to update to the official release once available

Please confirm whether this is intentional and temporary, or if this should be reverted to a stable version.

packages/console/app/package.json (1)

7-10: LGTM! Scripts updated for Vite tooling.

The migration from Vinxi to Vite commands is consistent across dev, build, and start scripts.

packages/console/app/vite.config.ts (1)

19-24: Minification disabled - verify this is intentional.

Disabling minification (minify: false) will result in larger bundle sizes and expose more implementation details in production builds.

Is this intentional for debugging purposes, or should minification be enabled for production builds? Consider using environment-based configuration:

minify: process.env.NODE_ENV === 'production'
packages/enterprise/tsconfig.json (1)

14-14: LGTM! Ambient types updated for Vite migration.

The change from vinxi/types/client to vite/client correctly aligns with the broader migration from Vinxi to Vite tooling.

packages/enterprise/vite.config.ts (1)

6-18: Good pattern for environment-specific configuration.

The dynamic nitroConfig based on OPENCODE_DEPLOYMENT_TARGET is a clean approach for managing deployment-specific settings.

packages/enterprise/package.json (1)

8-11: LGTM! Scripts migrated to Vite with Cloudflare-specific build.

The migration from Vinxi to Vite commands is consistent, and the addition of build:cloudflare aligns well with the conditional configuration in vite.config.ts.

packages/console/app/src/middleware.ts (1)

1-5: Code change verified as correct.

The migration to createMiddleware is the correct approach for the current @solidjs/start version. The SolidStart middleware API uses createMiddleware imported from "@solidjs/start/middleware", which matches your implementation exactly.

packages/opencode/src/util/locale.ts (1)

6-16: Locale time/date helpers look correct and consistent

time, datetime, and todayTimeOrDateTime are locale-aware, use the same Date basis, and cleanly separate “today” vs full date-time display. The APIs are straightforward and align with their intended usage in the TUI.

Also applies to: 18-29

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (1)

910-912: UserMessage timestamp rendering via showTimestamps and Locale looks good

Using ctx.showTimestamps() to choose between Locale.todayTimeOrDateTime and Locale.time cleanly matches the new UX: full date when enabled, short time otherwise, while still respecting the queued state. The first non-synthetic text part lookup is also consistent with existing behavior.

Also applies to: 966-975

packages/opencode/src/session/system.ts (1)

16-17: Compaction system prompt wiring is consistent with summarize/title

The new SystemPrompt.compaction(providerID) helper and its import cleanly mirror the existing summarize/title behavior (special-casing "anthropic", otherwise returning a single compaction prompt). This keeps provider-specific spoofing centralized and looks correct as written.

Also applies to: 120-127

packages/opencode/src/session/summary.ts (1)

75-82: Small-model provider options are composed consistently across summary flows

The new options pipeline that combines:

  • ProviderTransform.options(...),
  • ProviderTransform.smallOptions(...), and
  • small.info.options,

then feeds that into ProviderTransform.providerOptions(...) for both title generation and the later summarization call, gives you a single, consistent place to encode small‑model tweaks (reasoning effort, thinking config, etc.). This matches the compaction/prompt flows and should reduce configuration drift.

No issues from what’s visible here.

Also applies to: 88-88, 134-143

packages/opencode/src/provider/transform.ts (1)

131-171: Always returning an options object simplifies downstream merging

Changing options(...) to always return a Record<string, any> (via a local result and unconditional return result) matches how it’s now consumed in the pipe/mergeDeep pipelines and avoids undefined checks at call sites.

The internal logic for setting usage, promptCacheKey, thinkingConfig, and gpt‑5‑specific knobs is unchanged and looks consistent.

import { createAsync, query, redirect } from "@solidjs/router"
import { Title, Meta, Link } from "@solidjs/meta"
import { HttpHeader } from "@solidjs/start"
// import { HttpHeader } from "@solidjs/start"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Clarify the intent and verify caching impact.

The HttpHeader component that was setting Cache-Control headers for CDN/browser caching has been commented out. This will disable caching for this landing page, which could impact performance.

Please clarify:

  1. Is this change temporary (for debugging) or permanent?
  2. If permanent, what is the rationale for disabling caching on the landing page?
  3. If temporary, consider adding a TODO comment explaining the intent.
  4. If permanent, remove the commented code rather than leaving it in place.

Run the following to check if HttpHeader is still used elsewhere in the console app:

Also applies to: 32-32


🏁 Script executed:

#!/bin/bash
# Search for HttpHeader usage in console app routes
rg -n --type=tsx --type=ts 'HttpHeader' packages/console/app/src/routes/

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for HttpHeader usage across console app without type restriction
rg -n 'HttpHeader' packages/console/app/src/routes/

Length of output: 517


🏁 Script executed:

#!/bin/bash
# Check the full context of the zen/index.tsx file to see the commented code
cat packages/console/app/src/routes/zen/index.tsx | head -40

Length of output: 1927


🏁 Script executed:

#!/bin/bash
# Search for HttpHeader imports/usage in the entire packages/console directory
rg -n 'HttpHeader' packages/console/app/src/ --max-count=20

Length of output: 517


Clarify the intent and verify caching impact—pattern affects both landing page routes.

The HttpHeader component setting Cache-Control headers has been commented out in both landing page routes (zen/index.tsx and index.tsx). This disables caching for these pages, which could impact performance. Both files use identical cache directives: "public, max-age=1, s-maxage=3600, stale-while-revalidate=86400".

Please clarify:

  1. Is this change temporary (for debugging) or permanent?
  2. If permanent, what is the rationale for disabling caching?
  3. If temporary, add a TODO comment explaining the intent.
  4. If permanent, remove the commented code rather than leaving it in place.
🤖 Prompt for AI Agents
In packages/console/app/src/routes/zen/index.tsx around line 4 the HttpHeader
import and Cache-Control setup was commented out, which disables caching for
this landing page (same pattern exists in index.tsx); decide whether this is
temporary or permanent: if temporary, restore or keep commented but add a TODO
comment explaining it's for debugging and when caching should be re-enabled,
include the original cache directive string ("public, max-age=1, s-maxage=3600,
stale-while-revalidate=86400") in the TODO and leave a reference to the matching
file so reviewers know both routes are affected; if permanent, delete the
commented-out import and any commented cache code from both zen/index.tsx and
index.tsx to avoid stale dead code and update PR description to state caching
was intentionally disabled.

Comment on lines 16 to 18
server: {
allowedHosts: true,
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Security: allowedHosts: true disables host header validation.

Setting allowedHosts: true disables Vite's host header validation, which can expose the development server to DNS rebinding attacks.

For local development, consider using a specific allowed hosts list:

-  server: {
-    allowedHosts: true,
-  },
+  server: {
+    allowedHosts: ["localhost", "0.0.0.0", ".opencode.ai"],
+  },

Or document why this broad permission is necessary.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
server: {
allowedHosts: true,
},
server: {
allowedHosts: ["localhost", "0.0.0.0", ".opencode.ai"],
},
🤖 Prompt for AI Agents
In packages/console/app/vite.config.ts around lines 16 to 18, the config sets
server.allowedHosts = true which disables Vite's host header validation and is a
security risk; replace the broad true with a specific list of allowed hosts
(e.g., ['localhost', '127.0.0.1', '::1', 'your-dev-host.example']) or set it to
false/default to re-enable validation, and if you truly need the broad setting
add an inline comment and project documentation explaining why and the
mitigations in place.

Comment on lines 22 to 25
server: {
host: "0.0.0.0",
allowedHosts: true,
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Security: allowedHosts: true disables host header validation.

Same security concern as in the console app config. Setting allowedHosts: true disables host header validation, exposing the development server to DNS rebinding attacks.

Consider using a specific allowed hosts list instead:

-  server: {
-    host: "0.0.0.0",
-    allowedHosts: true,
-  },
+  server: {
+    host: "0.0.0.0",
+    allowedHosts: ["localhost", "0.0.0.0", ".opencode.ai"],
+  },
🤖 Prompt for AI Agents
packages/enterprise/vite.config.ts around lines 22 to 25: currently
server.allowedHosts is set to true which disables host header validation; change
it to an explicit allowlist of hostnames/IPs your dev server must accept (or
remove the setting to use Vite's default validation) so DNS rebinding is not
possible — replace the boolean with a string array of trusted hosts (e.g.,
localhost and any required internal domains) or omit the option entirely.

Comment on lines 40 to 60
export function duration(input: number) {
if (input < 1000) {
return `${input}ms`
}
if (input < 60000) {
return `${(input / 1000).toFixed(1)}s`
}
if (input < 3600000) {
const minutes = Math.floor(input / 60000)
const seconds = Math.floor((input % 60000) / 1000)
return `${minutes}m ${seconds}s`
}
if (input < 86400000) {
const hours = Math.floor(input / 3600000)
const minutes = Math.floor((input % 3600000) / 60000)
return `${hours}h ${minutes}m`
}
const hours = Math.floor(input / 3600000)
const days = Math.floor((input % 3600000) / 86400000)
return `${days}d ${hours}h`
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix day/hour calculation in Locale.duration for inputs ≥ 24h

The final branch currently mixes up the modulo divisors, so multi-day durations will render as 0d <totalHours>h instead of <days>d <remainingHours>h. For example, 2 days 3 hours becomes 0d 51h.

You can fix the computation like this:

-  export function duration(input: number) {
+  export function duration(input: number): string {
@@
-    if (input < 86400000) {
+    if (input < 86400000) {
@@
-    const hours = Math.floor(input / 3600000)
-    const days = Math.floor((input % 3600000) / 86400000)
-    return `${days}d ${hours}h`
+    const days = Math.floor(input / 86400000)
+    const hours = Math.floor((input % 86400000) / 3600000)
+    return `${days}d ${hours}h`
   }

This preserves existing behavior for sub-day durations and corrects the day/hour split for longer ones.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function duration(input: number) {
if (input < 1000) {
return `${input}ms`
}
if (input < 60000) {
return `${(input / 1000).toFixed(1)}s`
}
if (input < 3600000) {
const minutes = Math.floor(input / 60000)
const seconds = Math.floor((input % 60000) / 1000)
return `${minutes}m ${seconds}s`
}
if (input < 86400000) {
const hours = Math.floor(input / 3600000)
const minutes = Math.floor((input % 3600000) / 60000)
return `${hours}h ${minutes}m`
}
const hours = Math.floor(input / 3600000)
const days = Math.floor((input % 3600000) / 86400000)
return `${days}d ${hours}h`
}
export function duration(input: number): string {
if (input < 1000) {
return `${input}ms`
}
if (input < 60000) {
return `${(input / 1000).toFixed(1)}s`
}
if (input < 3600000) {
const minutes = Math.floor(input / 60000)
const seconds = Math.floor((input % 60000) / 1000)
return `${minutes}m ${seconds}s`
}
if (input < 86400000) {
const hours = Math.floor(input / 3600000)
const minutes = Math.floor((input % 3600000) / 60000)
return `${hours}h ${minutes}m`
}
const days = Math.floor(input / 86400000)
const hours = Math.floor((input % 86400000) / 3600000)
return `${days}d ${hours}h`
}
🤖 Prompt for AI Agents
In packages/opencode/src/util/locale.ts around lines 40 to 60, the final branch
calculating days and hours uses incorrect modulo divisors producing outputs like
"0d 51h" for multi-day durations; change it to compute days = Math.floor(input /
86400000) and hours = Math.floor((input % 86400000) / 3600000) so days count
whole 24h blocks and hours are the remaining hours within the day, then return
`${days}d ${hours}h`.

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.

3 participants