Skip to content

sync(upstream): permission config + provider/runtime fixes (PR5/5)#272

Closed
Astro-Han wants to merge 8 commits into
claude/upstream-sync-209-pr4-tool-frameworkfrom
claude/upstream-sync-209-pr5-permission
Closed

sync(upstream): permission config + provider/runtime fixes (PR5/5)#272
Astro-Han wants to merge 8 commits into
claude/upstream-sync-209-pr4-tool-frameworkfrom
claude/upstream-sync-209-pr5-permission

Conversation

@Astro-Han

Copy link
Copy Markdown
Owner

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 F — Permission config (1 file)
    • #24222 fix permission config order (drop wildcard sort, preserve user key order)
    • #24235 IntelliSense for all tool permission keys via InfoZod override
  • Stage G.1 — Provider tail (8 files)
    • DeepSeek (#24146 #24157 #24163 #24180), OpenRouter sdk bump #24435, Codex #23925 (already incorporated in PawWork's helper), Mistral #23735, Kimi #23696, Vertex #24573, OpenAI #24063 #24212, reasoning transforms split #24574
  • Stage G.2 — Runtime (2 files)
    • #24215 shell cwd: pass cwd as positional arg ('$1') instead of capturing $PWD pre-init
    • #23770 `tool_output` config schema (max_lines / max_bytes)
    • #23797 BOM, #19054 bare-repo, #23870 compaction already in HEAD (no-op)
  • Stage I — SDK regen (1 file: types.gen.ts)
    • Reflects PermissionConfig intersection record + Config.tool_output

Stage G.3 + H — deferred to follow-up

  • G.3 (#24482 agent-create deny default): upstream rewrote `agent.ts` from tools-based to permissions-based selection; PawWork still uses tools-based path. Fix needs reapplication on PawWork's own structure.
  • H (#20602 shell selection): upstream rewrote `shell.ts` BLACKLIST/LOGIN/POSIX into META dict + heavy pty refactor; PawWork has structural divergence. Slice doesn't compose without porting full upstream architecture.

Both tracked in follow-up task to re-sync tool .ts files (descriptions stay carve-out, logic syncs upstream).

Carve-outs preserved

  • `packages/opencode/src/permission/index.ts` — kept on PawWork (legacy task→agent alias, list() endpoint)
  • `packages/opencode/src/plugin/codex.ts` — kept on PawWork (helper refactor, functionally equivalent)
  • `packages/opencode/src/session/compaction.ts` + `agent/prompt/compaction.txt` — kept on PawWork (anchored summary template diverged from upstream redesign)
  • `packages/opencode/src/{format,patch}/index.ts`, `session/message-v2.ts`, `tool/{bash,write,edit,truncate,apply_patch}.ts` — kept on PawWork structure (namespace wrappers, Zod params, divergent format.file API)
  • `packages/opencode/package.json` — kept PawWork workspace deps; only @openrouter bumped to 2.8.1
  • `packages/app/src/components/settings-general.tsx` + `packages/app/src/i18n/en.ts` + `packages/web/src/content/docs/*.mdx` — dropped per UI rewrite + web docs carve-out
  • Tests kept on HEAD where PawWork tests have diverged

Test plan

  • `bun run --cwd packages/opencode typecheck` — clean
  • `bun run --cwd packages/core typecheck` — clean
  • `bun run --cwd packages/util typecheck` — clean
  • `bun run --cwd packages/sdk/js typecheck` — clean
  • `bun run --cwd packages/app typecheck` — clean
  • Carve-out leak guard run on every stage commit
  • Reviewer to verify permission config IntelliSense in editor
  • Reviewer to verify shell cwd fix in interactive bash session

Refs #209

Astro-Han and others added 8 commits April 27, 2026 15:57
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
@Astro-Han Astro-Han added P3 Low priority upstream Tracked upstream or vendor behavior labels Apr 27, 2026
@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4f29cdd2-e775-4172-949d-e33e05bf348a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/upstream-sync-209-pr5-permission

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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
  1. When filtering by version, prefer semantic version comparison over hardcoded allowlists to automatically support future versions.

Comment on lines +200 to +215
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: "" },
],
}
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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.

Suggested change
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 {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

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
  1. When filtering by version, prefer semantic version comparison over hardcoded allowlists to automatically support future versions.
  2. 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).

@Astro-Han Astro-Han force-pushed the claude/upstream-sync-209-pr4-tool-framework branch 12 times, most recently from 3155c4d to 2b69f7f Compare April 28, 2026 08:28
@Astro-Han Astro-Han deleted the branch claude/upstream-sync-209-pr4-tool-framework April 28, 2026 08:56
@Astro-Han Astro-Han closed this Apr 28, 2026
@Astro-Han Astro-Han deleted the claude/upstream-sync-209-pr5-permission branch May 2, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low priority upstream Tracked upstream or vendor behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants