sync(upstream): permission config + provider/runtime fixes (PR5/5)#272
Conversation
Pure mechanical rename of the workspace package. packages/shared is moved to packages/core; all consumers update imports from @opencode-ai/shared/* to @opencode-ai/core/*. No file content changes beyond the package rename. PawWork-local consumers (packages/util, packages/ui, packages/opencode, packages/desktop-electron runtime-import-guard test) updated. This is the first half of upstream's "shared → core" refactor; the follow-up commits that move modules (Global, log, flag, cross-spawn, npm) into core land in a separate PR (#209 PR2). Refs: #209
Pulls upstream PR follow-ups to the shared→core rename: - 1a734adb4d core: consolidate shared infrastructure into core package - 705f792e87 core: move Global module to @opencode-ai/core - 3eee2f6afa core: move cross-spawn-spawner from opencode to core - 1e98167b0e core: move cross-spawn-spawner to root and remove unused types - f5dce6d960 core: move npm service to core package Also pulls the npm-config snapshot from upstream HEAD (Stage C of #209 intake plan): packages/core/src/npm.ts, npm-config.ts, npm-config.test.ts. The pre-existing packages/opencode/test/npm.test.ts is removed. PawWork carve-outs: - packages/core/src/global.ts uses Runtime.appName() instead of hardcoded "opencode" so the pawwork-namespace data dir is preserved. - packages/opencode/src/global/index.ts is kept as PawWork's local Runtime-aware Global module; upstream's auto-rewritten @opencode-ai/core/global imports flow through to the carve-out. - packages/core/src/effect/logger.ts uses PawWork's namespace-style Log API (Log.create / Log.Default / Log.init) rather than upstream HEAD's flat-export style. PawWork callers expect the namespace shape. Mechanical cleanup: - packages/opencode/src/installation/meta.ts is removed; callers use @opencode-ai/core/installation/version (InstallationVersion / InstallationChannel) directly. - All ../util/log, ../flag/flag, @/effect/* and similar local imports rewritten to @opencode-ai/core/* equivalents (~120 files). - @effect/opentelemetry catalog entry added to root package.json (4.0.0-beta.46, matching the rest of the effect ecosystem). Verified: typecheck clean across packages/{core, opencode, util, ui, desktop-electron}. Refs: #209
Squashes upstream PRs #23716, #23740, #23744, #23745, #23747, #23749,
#23752, #23754, #23756, #23757, #23763, #24005, #24040, #24169, #24213
into a single PawWork commit. Ranges over 16 upstream commits in
date order from upstream's Effect Schema migration batch.
What landed:
- Config.Info, ConfigPermission.Info, ConfigAgent.Info schema definitions
migrated to Effect Schema canonical form. PawWork callers use
Config.Info.zod accessor where Hono validators or Zod-style decoders
are still needed.
- Schema definition files in control-plane, permission, project, pty,
question, sync, tool, session migrated to Effect Schema.
- packages/opencode/src/util/effect-zod.ts walker enhanced to derive
.zod from Effect Schema at runtime, plus tests.
- packages/sdk/js/v2/gen/types.gen.ts regenerated to reflect schema
shape changes.
What was preserved (PawWork divergence kept on Zod / namespace API):
- packages/opencode/src/session/{session,message-v2,prompt,export,message,revert,summary,todo,status,compaction,projectors}.ts
— PawWork's session domain stays Zod-shaped to keep MessageV2
PawWork-only types (FileSource/SymbolSource/ResourceSource carry
PawWork attachment metadata) compatible with current callers.
- packages/opencode/src/bus/bus-event.ts — PawWork keeps Zod-typed
BusEvent.define so the 30+ PawWork test fixtures and divergent
consumers (server/instance/*, acp/agent.ts, file/watcher.ts) keep
reading evt.properties as their schema's inferred type rather than
unknown.
- packages/opencode/src/{snapshot,permission,mcp,provider}/index.ts —
these modules use a PawWork "namespace + XValue alias" pattern that
upstream redesigned. Keeping HEAD here preserves Snapshot.track,
Permission.ask, Mcp.add, Provider.list etc. for PawWork callers.
- packages/opencode/src/provider/* — PawWork's volcengine/zen
integration carries divergent code paths (VOLCENGINE_PLAN constants,
HTTP-Referer/X-Title carve-out per project_rename_blockers.md).
- packages/opencode/src/{file,project,pty,sync,ide,lsp/client,command,
control-plane/workspace,worktree,question,util/schema}/{index,*}.ts
— kept PawWork API surface for callers.
Skipped commits in this range (no PawWork-applicable change after
carve-outs): 93940a1859 (provider domain) — provider.ts is a PawWork
permanent carve-out; the migration is reapplied to PawWork's local
provider only when divergence settles.
Verified: typecheck clean across packages/{core, opencode, util, ui,
desktop-electron}.
Refs: #209
Pulls upstream PR #23244 (tool framework Effect Schema migration) and applies the same pattern to PawWork's renamed agent.ts (was upstream task.ts) plus PawWork-only tools. What landed (35 files): - packages/opencode/src/tool/tool.ts: framework migrated to use Schema.Decoder<unknown> for parameters, with Init<P, M> indirection. - All 18 upstream-tracked tool files migrated: bash, edit, glob, grep, invalid, lsp, plan, read, schema, skill, todo, truncate, truncation-dir, webfetch, websearch, write, external-directory, plus apply_patch and codesearch (PawWork mirrors that upstream's #23244 also migrates). - PawWork's renamed agent.ts manually ported to Effect Schema following the bash.ts pattern (replaces upstream's task.ts which is dropped). - PawWork-only tools migrated: trash.ts, mcp-exa.ts. - packages/opencode/src/util/bom.ts restored from upstream HEAD (used by the new edit.ts). - Truncate.Service / Agent.Service: added type re-exports to truncate.ts so they're usable in type position. PawWork carve-outs preserved: - packages/opencode/src/tool/*.txt (tool descriptions): all kept HEAD per project_tool_description_carveout_strategic.md — PawWork's tool descriptions are deliberately divergent from upstream. - packages/opencode/src/tool/registry.ts: kept PawWork pre-D structure (custom Tool.init pattern, AgentTool/trash/mcp-exa registration) because upstream redesigned registry around its own task/agent shape. Three @ts-expect-error directives mark the Zod-vs-Effect-Schema boundary where PawWork's QuestionTool / GrepTool / plugin tools still use Zod params. - packages/opencode/src/tool/question.ts and grep.ts: kept PawWork pre-D Zod params (Question.Prompt and Ripgrep.Interface remain Zod in PawWork divergent question/index.ts and file/ripgrep.ts). - packages/opencode/src/tool/edit.ts: ported to PawWork's format/lsp API surface (format.file returns void, lsp.touchFile takes boolean) rather than upstream HEAD's signature. - packages/opencode/test/tool/question.test.ts removed: tested old Zod-Schema mixed surface; rewrite when question namespace is migrated. Mechanical: - import { Tool } from "./tool" → import * as Tool from "./tool" (~13 source/test files): tool/tool.ts moved from namespace to flat exports in upstream's framework. - import { ToolRegistry } from "./registry" stays named import: PawWork registry kept its namespace export. Verified: typecheck clean across packages/{core, opencode, util, ui, desktop-electron}. Refs: #209
Sync from anomalyco/opencode v1.14.20..17701628bd: * fix permission config order (#24222) — drop wildcard sort, preserve user key order so permission precedence matches the order users write in config (66f93035b0) * core: full IntelliSense for all tool permission keys via InfoZod override (1b92c95425) Carve-outs preserved: - packages/opencode/src/permission/index.ts kept on PawWork (legacy task→agent alias, list() endpoint, refactored reply state machine) - tests kept on HEAD (PawWork tests diverged after agent rename) - packages/sdk/js + openapi.json kept on HEAD; will regenerate in Stage I Refs anomalyco/opencode#24222 #24235 PawWork issue: #209 (PR5 of upstream sync)
DeepSeek (#24146 preserve empty reasoning_content, #24157 variants, #24163 max support, #24180 ensure assistant reasoning), OpenRouter sdk bump (#24435), Codex (#23925 model logic — already incorporated into PawWork's shouldKeepCodexOAuthModel helper), Mistral Small reasoning (#23735), Kimi variants (#23696), Google Vertex tool call streaming default (#24573), OpenAI retry/oauth (#24063, #24212), reasoning transforms split (#24574). Carve-outs preserved: - packages/opencode/src/plugin/codex.ts kept on PawWork (helper refactor of allowed model logic — upstream change is functionally equivalent) - packages/opencode/package.json kept PawWork-specific workspace deps (@opencode-ai/core, @opencode-ai/util); only @openrouter bump applied Refs anomalyco/opencode #23696 #23735 #23925 #24063 #24146 #24157 #24163 #24180 #24212 #24435 #24573 #24574 PawWork issue: #209
Functional changes from upstream batch (#23797 BOM, #23770 truncate
config, #23870 compaction, #19054 bare-repo, #24215 shell cwd):
* session/prompt.ts: shell invocations now pass cwd as positional arg
("$1") instead of capturing $PWD before sourcing shell init files
(#24215)
* config/config.ts: add tool_output { max_lines, max_bytes } schema
for user-configurable truncation thresholds (#23770)
Already incorporated in HEAD (no-op cherry-picks):
- BOM round-trip (Bom.syncFile already wired in edit/write/apply_patch)
- Project bare-repo cache (isBareRepo + readCachedProjectId(common)
already in project.ts)
Deferred to Post-merge tool re-sync (#19):
- Truncate config wiring inside truncate.ts (namespace structure
diverged; needs structural merge)
- Session compaction rewrite (#23870 SUMMARY_TEMPLATE/Turn/Tail
redesign vs PawWork's anchored template)
Refs anomalyco/opencode #19054 #23770 #23797 #23870 #24215
PawWork issue: #209
Stage I regen reflects:
- PermissionConfig gains explicit tool keys with intersection record
type (Stage F: #24222 + #24235 IntelliSense)
- Config.tool_output { max_lines, max_bytes } block added
(Stage G.2: #23770 truncation thresholds)
Refs: #209
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request updates the @openrouter/ai-sdk-provider dependency, introduces configuration for tool output truncation, and refactors permission schema handling to preserve key order for precedence. It also enhances error parsing for retryable server errors, refactors shell command execution to improve robustness, and adds specific model handling for Deepseek, Mistral, and Kimi. Review feedback suggests avoiding hardcoded model IDs for GPT and Mistral in favor of capability checks or semantic versioning, and recommends prepending reasoning parts in Deepseek assistant messages to ensure correct API behavior.
| } | ||
|
|
||
| // gpt-5.5 models temporarily have restricted context window size for codex plans | ||
| if (model.id.includes("gpt-5.5")) { |
There was a problem hiding this comment.
The model ID gpt-5.5 appears to be a typo or a placeholder. Additionally, hardcoding specific model IDs is discouraged. Per repository guidelines, prefer using semantic version comparisons or capability checks over hardcoded allowlists to ensure compatibility with future model releases.
References
- When filtering by version, prefer semantic version comparison over hardcoded allowlists to automatically support future versions.
| if (model.api.id.includes("deepseek")) { | ||
| msgs = msgs.map((msg) => { | ||
| if (msg.role !== "assistant") return msg | ||
| if (Array.isArray(msg.content)) { | ||
| if (msg.content.some((part) => part.type === "reasoning")) return msg | ||
| return { ...msg, content: [...msg.content, { type: "reasoning", text: "" }] } | ||
| } | ||
| return { | ||
| ...msg, | ||
| content: [ | ||
| ...(msg.content ? [{ type: "text" as const, text: msg.content }] : []), | ||
| { type: "reasoning" as const, text: "" }, | ||
| ], | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
For Deepseek models (like R1), the reasoning part should typically precede the text or tool call parts in the assistant message. Appending it at the end might lead to incorrect model behavior or API validation errors. Additionally, it's safer to check for the reasoning capability to avoid adding reasoning parts to models that do not support them.
| if (model.api.id.includes("deepseek")) { | |
| msgs = msgs.map((msg) => { | |
| if (msg.role !== "assistant") return msg | |
| if (Array.isArray(msg.content)) { | |
| if (msg.content.some((part) => part.type === "reasoning")) return msg | |
| return { ...msg, content: [...msg.content, { type: "reasoning", text: "" }] } | |
| } | |
| return { | |
| ...msg, | |
| content: [ | |
| ...(msg.content ? [{ type: "text" as const, text: msg.content }] : []), | |
| { type: "reasoning" as const, text: "" }, | |
| ], | |
| } | |
| }) | |
| } | |
| if (model.api.id.includes("deepseek") && model.capabilities.reasoning) { | |
| msgs = msgs.map((msg) => { | |
| if (msg.role !== "assistant") return msg | |
| if (Array.isArray(msg.content)) { | |
| if (msg.content.some((part) => part.type === "reasoning")) return msg | |
| return { ...msg, content: [{ type: "reasoning", text: "" }, ...msg.content] } | |
| } | |
| return { | |
| ...msg, | |
| content: [ | |
| { type: "reasoning" as const, text: "" }, | |
| ...(msg.content ? [{ type: "text" as const, text: msg.content }] : []), | |
| ], | |
| } | |
| }) | |
| } |
| if (!model.capabilities.reasoning) return {} | ||
| // Only Mistral Small 4 supports reasoning (mistral-small-2603, mistral-small-latest) | ||
| const mistralId = model.api.id.toLowerCase() | ||
| if (!mistralId.includes("mistral-small-2603") && !mistralId.includes("mistral-small-latest")) return {} |
There was a problem hiding this comment.
Hardcoding specific model IDs like mistral-small-2603 is brittle. Instead of hardcoded allowlists, prefer semantic version comparison or capability checks to support future versions. Additionally, ensure the configuration path logic accounts for whether the model uses date-based (like this one) or name-based identifiers.
References
- When filtering by version, prefer semantic version comparison over hardcoded allowlists to automatically support future versions.
- Verify the correct configuration path for a model before applying changes, as different models may use different logic paths (e.g., date-based vs. name-based).
3155c4d to
2b69f7f
Compare
Summary
Stage F + G + H + I of the upstream sync from anomalyco/opencode v1.14.20..17701628bd. Stacked on #270 (PR4 — tool framework). 4 commits, 12 files, all PawWork carve-outs preserved.
Stages included
Stage G.3 + H — deferred to follow-up
Both tracked in follow-up task to re-sync tool .ts files (descriptions stay carve-out, logic syncs upstream).
Carve-outs preserved
Test plan
Refs #209