feat(guardrails): plan→auto chain + upstream sync infra + v1.4.1 merge#151
feat(guardrails): plan→auto chain + upstream sync infra + v1.4.1 merge#151
Conversation
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
…ped in sdk and openapi schema (anomalyco#21543)
…nomalyco#19955) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
Co-authored-by: Frank <frank@anoma.ly>
…c infra - Fix duplicate hooks key in .opencode/opencode.jsonc (guardrails.sh was silently lost) - Add OPENCODE_EXPERIMENTAL_PLAN_MODE to guardrails wrapper (enables PlanExitTool) - Implement plan→auto chain in tool.execute.after (Issue #148) with phase guard + try/catch - Improve redirect regex: /\s>\s*[\/~$._a-zA-Z]|^>/ — detects file redirects, avoids comparison false positives (9/9 tests) - Add dev:guardrails script for local dev DX - Add .gitattributes with merge=ours for fork-only paths (auto-protect during upstream merge) - Add scripts/upstream-sync.sh for selective upstream merge automation - Add FORK.md documenting fork-only systems and sync strategy Closes #148 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge upstream/dev (22 commits) using .gitattributes merge=ours for fork-only paths. Fork-only systems (hook, memory, guardrails, notification) auto-protected. Conflicts resolved manually: - packages/opencode/src/session/processor.ts: kept both imports (fork repetition + upstream isRecord) - packages/opencode/src/session/prompt.ts: kept fork memory injection + adopted upstream toModelMessagesEffect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
New PR opened -- automated review will run on the next push. To trigger a manual review, comment |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
The following comment was made by an LLM, it may be inaccurate: |
…sktop app Desktop app and bare `opencode` CLI read commands from `.opencode/command/` and agents from `.opencode/agent/`, NOT from the guardrails profile. This caused /auto, /plan, /ship, etc. to be invisible outside the wrapper. Fix: symlink 25 commands and 33 agents from guardrails profile into .opencode/ so they're available to ALL launch methods (desktop, CLI, wrapper). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR merges upstream v1.4.1 into the fork while adding fork-specific infrastructure for future upstream syncs and improving guardrails/workflow ergonomics (notably /plan → /auto chaining), plus a set of server/tooling refactors and UI/test updates to match the new behaviors.
Changes:
- Add upstream sync tooling (
.gitattributesmerge=ours +scripts/upstream-sync.sh) and document fork-only areas (FORK.md). - Improve runtime behavior around tools/workflows (Task tool refactor, abort propagation, provider-executed tool handling,
/plan→implementingchain). - Update server/desktop/web UI integration (Server.Default API shape, websocket proxying, new desktop-electron embedded server approach) and bump versions to 1.4.1.
Reviewed changes
Copilot reviewed 165 out of 171 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| sdks/vscode/package.json | Bump VS Code extension version to 1.4.1. |
| scripts/upstream-sync.sh | New selective upstream merge helper script. |
| packages/web/package.json | Bump web package version to 1.4.1. |
| packages/util/package.json | Bump util package version to 1.4.1. |
| packages/ui/src/components/session-turn.tsx | Adjust sticky accordion offset. |
| packages/ui/src/components/session-turn.css | Make diffs header sticky and adjust layout. |
| packages/ui/src/components/session-review.tsx | Harden diffs input handling via type guards/normalization. |
| packages/ui/src/components/message-part.tsx | Route apply_patch rendering through normalized patch file parsing. |
| packages/ui/src/components/apply-patch-file.ts | New patch metadata normalizer to ViewDiff for UI diffs. |
| packages/ui/src/components/apply-patch-file.test.ts | New unit tests for patch metadata parsing/legacy payloads. |
| packages/ui/package.json | Bump UI package version to 1.4.1. |
| packages/slack/package.json | Bump slack package version to 1.4.1. |
| packages/sdk/openapi.json | Update OpenAPI schemas (assistant message shape, provider schema ref, provider config fields). |
| packages/sdk/js/src/v2/gen/types.gen.ts | Regenerate SDK types to match OpenAPI updates (provider config, hooks/memory, response shapes). |
| packages/sdk/js/package.json | Bump JS SDK version to 1.4.1. |
| packages/plugin/package.json | Bump plugin package version to 1.4.1. |
| packages/opencode/test/tool/webfetch.test.ts | Update webfetch tests to use Bun.serve and remove timeout test. |
| packages/opencode/test/tool/task.test.ts | Expand Task tool tests (resume/create child, permission shaping, bypass checks). |
| packages/opencode/test/storage/json-migration.test.ts | Update migration tests to pass drizzle DB (not raw sqlite client). |
| packages/opencode/test/session/prompt-effect.test.ts | Refactor task tool mocking via ToolRegistry + add abort propagation tests for inline read tool. |
| packages/opencode/test/session/processor-effect.test.ts | Add tests for text timing + interrupted tool metadata and cleanup behavior. |
| packages/opencode/test/session/message-v2.test.ts | Add test to forward partial tool output on abort. |
| packages/opencode/test/session/compaction.test.ts | Remove now-nonexistent processor.abort in fake. |
| packages/opencode/test/server/session-select.test.ts | Adjust for Server.Default now returning object with .app. |
| packages/opencode/test/server/session-messages.test.ts | Adjust for Server.Default now returning object with .app. |
| packages/opencode/test/server/session-actions.test.ts | Adjust for Server.Default now returning object with .app. |
| packages/opencode/test/server/project-init-git.test.ts | Adjust for Server.Default now returning object with .app. |
| packages/opencode/src/tool/tool.ts | Improve Tool typing (InferDef, typed id for define/defineEffect, typed init). |
| packages/opencode/src/tool/task.ts | Refactor Task tool to defineEffect; centralize parameters; Effect-based execution and improved session handling. |
| packages/opencode/src/tool/registry.ts | Replace fromID with named tool access; pre-init builtins; add Agent layer dependency. |
| packages/opencode/src/tool/read.ts | Simplify symlink stat handling with Effect.catch + void return. |
| packages/opencode/src/storage/json-migration.ts | Change migration API to accept drizzle DB and run PRAGMA/transactions through it. |
| packages/opencode/src/shell/shell.ts | Switch to internal which() and adjust Windows shell detection. |
| packages/opencode/src/session/retry.ts | Export GO upsell message constant for reuse by TUI logic. |
| packages/opencode/src/session/prompt.ts | Use ToolRegistry.named; improve read tool execution for file parts; avoid re-looping provider-executed tools; restructure ensuring/finalizers. |
| packages/opencode/src/session/processor.ts | Track provider-executed tool metadata; preserve text start time; mark interrupted tools with metadata; change interrupt handling. |
| packages/opencode/src/session/message-v2.ts | Strip providerExecuted from provider metadata; forward partial output for interrupted tool errors; propagate providerExecuted flag. |
| packages/opencode/src/session/llm.ts | Add workflow model session context + approval handler wiring for tool approvals. |
| packages/opencode/src/session/compaction.ts | Remove dependence on processor.abort for interrupts. |
| packages/opencode/src/server/server.ts | Server.Default now returns server object; add auth_token query handling for basic auth; reorder imports. |
| packages/opencode/src/server/routes/session.ts | Simplify prompt_async endpoint; update OpenAPI response schema to WithParts. |
| packages/opencode/src/server/routes/provider.ts | Update provider route schema to Provider.Info. |
| packages/opencode/src/server/router.ts | Pass upgrade into websocket proxy call. |
| packages/opencode/src/server/proxy.ts | Move websocket upgrade wiring to injected UpgradeWebSocket (no bun-only upgradeWebSocket). |
| packages/opencode/src/server/instance.ts | Switch embedded UI file serving to node fs + explicit mime type detection. |
| packages/opencode/src/provider/transform.ts | Add big-pickle to variant bypass list. |
| packages/opencode/src/provider/provider.ts | Ensure ModelsDev→Provider model conversion aligns with snapshot schema changes. |
| packages/opencode/src/provider/models.ts | Remove options/headers/variants from ModelsDev.Model schema (align to upstream snapshot). |
| packages/opencode/src/provider/models-snapshot.d.ts | New generated types for models snapshot. |
| packages/opencode/src/project/project.ts | Simplify Effect.catch to Effect.void for project ID file read. |
| packages/opencode/src/plugin/index.ts | Update server fetch usage to Server.Default().app.fetch. |
| packages/opencode/src/node.ts | Export additional Node entrypoints (Config/bootstrap/Log/Database/JsonMigration). |
| packages/opencode/src/mcp/index.ts | Simplify Effect.catch to Effect.void in MCP create. |
| packages/opencode/src/lsp/server.ts | Narrow clangd root detection targets. |
| packages/opencode/src/lsp/index.ts | Add root to “spawned lsp server” log. |
| packages/opencode/src/index.ts | Pass drizzle DB to JsonMigration.run; other upstream merge adjustments. |
| packages/opencode/src/file/time.ts | Simplify stat error handling to Effect.void. |
| packages/opencode/src/config/config.ts | Rework Provider/Model schema definitions and provider options/whitelist/blacklist fields. |
| packages/opencode/src/cli/cmd/tui/worker.ts | Update Server.Default fetch usage to .app.fetch. |
| packages/opencode/src/cli/cmd/tui/routes/session/index.tsx | Add Go upsell dialog trigger based on retry message. |
| packages/opencode/src/cli/cmd/tui/component/dialog-go-upsell.tsx | New TUI dialog for Go upsell CTA. |
| packages/opencode/src/cli/cmd/run.ts | Update Server.Default fetch usage to .app.fetch. |
| packages/opencode/src/cli/cmd/db.ts | Pass drizzle DB to JsonMigration.run. |
| packages/opencode/script/generate.ts | New script to generate models snapshot artifacts. |
| packages/opencode/script/build.ts | Delegate models snapshot generation to generate.ts. |
| packages/opencode/script/build-node.ts | Build node distribution into dist/node; generate snapshot; adjust externals/files. |
| packages/opencode/package.json | Bump version to 1.4.1; update deps (node-pty via catalog, gitlab-ai-provider). |
| packages/opencode/.claude/state/tool-failure-counter.txt | New Claude state artifact file (likely accidental commit). |
| packages/opencode/.claude/state/test-recommendation.json | New Claude state artifact file (likely accidental commit). |
| packages/opencode/.claude/state/task-quality-gate.json | New Claude state artifact file (likely accidental commit). |
| packages/opencode/.claude/state/runtime-reactive.jsonl | New Claude state artifact file (likely accidental commit). |
| packages/opencode/.claude/state/breezing-timeline.jsonl | New Claude state artifact file (likely accidental commit). |
| packages/opencode/.claude/sessions/.last_inbox_check | New Claude state artifact file (likely accidental commit). |
| packages/guardrails/profile/plugins/guardrail.ts | Tighten redirect regex and add plan_exit → implementing chain logic. |
| packages/guardrails/bin/opencode-guardrails | Force-enable experimental plan mode for guardrails wrapper. |
| packages/function/package.json | Bump function package version to 1.4.1. |
| packages/extensions/zed/extension.toml | Bump Zed extension version + release URLs to 1.4.1 artifacts. |
| packages/enterprise/package.json | Bump enterprise package version to 1.4.1. |
| packages/desktop/package.json | Bump desktop package version to 1.4.1. |
| packages/desktop-electron/src/main/shell-env.ts | Rename constants/functions and standardize log prefixes. |
| packages/desktop-electron/src/main/server.ts | Replace sidecar CLI spawn with in-process virtual server module. |
| packages/desktop-electron/src/main/menu.ts | Remove “Install CLI…” menu item. |
| packages/desktop-electron/src/main/ipc.ts | Remove IPC handler for CLI installation. |
| packages/desktop-electron/src/main/index.ts | Wire new server lifecycle; remove sidecar/CLI sync logic; disable embedded web UI. |
| packages/desktop-electron/src/main/env.d.ts | Add virtual module typing for embedded server exports. |
| packages/desktop-electron/src/main/cli.ts | Deleted: old sidecar/CLI install + spawn implementation. |
| packages/desktop-electron/scripts/prepare.ts | Delegate to prebuild script; simplify prepare workflow. |
| packages/desktop-electron/scripts/predev.ts | Build embedded server via opencode build-node script. |
| packages/desktop-electron/scripts/prebuild.ts | New prebuild script (icons + build-node). |
| packages/desktop-electron/package.json | Bump version; restructure deps/devDeps; add optional node-pty platform deps. |
| packages/desktop-electron/electron.vite.config.ts | Add virtual server module resolver + copy wasm assets; narrow node-pty dependency by platform. |
| packages/desktop-electron/electron-builder.config.ts | Remove extraResources for opencode-cli sidecar artifact. |
| packages/console/mail/package.json | Bump console-mail version to 1.4.1. |
| packages/console/function/package.json | Bump console-function version to 1.4.1. |
| packages/console/core/package.json | Bump console-core version to 1.4.1. |
| packages/console/app/package.json | Bump console-app version to 1.4.1. |
| packages/app/src/utils/diffs.ts | New diff/summary normalization utilities for app UI. |
| packages/app/src/utils/diffs.test.ts | Tests for diffs/message normalization utilities. |
| packages/app/src/pages/session.tsx | Normalize session diffs and review diffs through shared utility. |
| packages/app/src/context/sync.tsx | Sanitize incoming message summaries and normalize diffs before storing. |
| packages/app/src/context/global-sync/event-reducer.ts | Normalize diffs and sanitize message.updated payloads. |
| packages/app/src/components/terminal.tsx | Add cross-origin websocket auth_token support and same-origin detection. |
| packages/app/src/app.tsx | Remove effectMinDuration usage from connection health check path. |
| packages/app/package.json | Bump app package version to 1.4.1. |
| package.json | Add dev:guardrails; add node-pty to workspace overrides. |
| nix/hashes.json | Update Nix nodeModules hashes. |
| FORK.md | New documentation for fork-only systems and upstream sync strategy. |
| bun.lock | Lockfile updates for version bumps/deps reshuffling. |
| .opencode/opencode.jsonc | Merge duplicate hooks keys; ensure guardrails hook retained. |
| .gitattributes | New merge=ours rules for fork-only paths during upstream sync. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (await fs.exists(match)) { | ||
| const mime = getMimeType(match) ?? "text/plain" | ||
| c.header("Content-Type", mime) | ||
| if (mime.startsWith("text/html")) { | ||
| c.header("Content-Security-Policy", DEFAULT_CSP) | ||
| } | ||
| return c.body(await file.arrayBuffer()) | ||
| return c.body(new Uint8Array(await fs.readFile(match))) |
There was a problem hiding this comment.
node:fs/promises does not provide an exists() API, so await fs.exists(match) will throw at runtime and break embedded Web UI file serving. Use fs.access/fs.stat with try/catch (or existsSync from node:fs) to perform an existence check instead.
| if (c.req.method === "OPTIONS") return next() | ||
| const password = Flag.OPENCODE_SERVER_PASSWORD | ||
| if (!password) return next() | ||
| const username = Flag.OPENCODE_SERVER_USERNAME ?? "opencode" | ||
|
|
||
| if (c.req.query("auth_token")) c.req.raw.headers.set("authorization", `Basic ${c.req.query("auth_token")}`) | ||
|
|
||
| return basicAuth({ username, password })(c, next) |
There was a problem hiding this comment.
Using auth_token as a query parameter to inject a Basic Authorization header can leak credentials via logs, browser history, proxies, and referrers. Consider scoping this to the WebSocket endpoints only and/or using a short-lived token mechanism (or a dedicated header/subprotocol) instead of passing credentials in the URL.
| next.searchParams.set("auth_token", btoa(`${username}:${password}`)) | ||
| // For same-origin requests, let the browser reuse the page's existing auth. | ||
| next.username = username | ||
| next.password = password |
There was a problem hiding this comment.
This branch sets both an auth_token query param and also embeds username/password into the WebSocket URL. If auth_token is the intended cross-origin auth mechanism, avoid also setting next.username/next.password (which can be exposed via URL inspection/debug tooling) and keep auth strictly in one place.
| next.searchParams.set("auth_token", btoa(`${username}:${password}`)) | |
| // For same-origin requests, let the browser reuse the page's existing auth. | |
| next.username = username | |
| next.password = password | |
| // For cross-origin requests, send auth only via the auth_token parameter. | |
| next.searchParams.set("auth_token", btoa(`${username}:${password}`)) |
| @@ -0,0 +1 @@ | |||
| 1 1775664051 | |||
There was a problem hiding this comment.
This looks like local Claude runtime state/artifact and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
| { | ||
| "timestamp": "2026-04-08T16:02:47Z", | ||
| "changed_file": "/Users/teradakousuke/Developer/opencode/packages/guardrails/profile/plugins/guardrail.ts", | ||
| "test_command": "npm test", | ||
| "related_test": "", | ||
| "recommendation": "テストの実行を推奨します" | ||
| } |
There was a problem hiding this comment.
This appears to be local Claude runtime state/artifact and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
| { | ||
| "7": { | ||
| "failure_count": 0, | ||
| "last_action": "reset", | ||
| "updated_at": "2026-04-08T08:34:25Z" |
There was a problem hiding this comment.
This appears to be local Claude runtime state/artifact and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
| {"event":"TaskCreated","timestamp":"2026-04-08T08:58:54Z","session_id":"185ec502-7ca0-4a88-8112-8389a55fa852","cwd":"/Users/teradakousuke/Developer/opencode/packages/opencode","file_path":"","previous_cwd":"","task_id":"8","task_title":""} | ||
| {"event":"TaskCreated","timestamp":"2026-04-08T08:58:57Z","session_id":"185ec502-7ca0-4a88-8112-8389a55fa852","cwd":"/Users/teradakousuke/Developer/opencode/packages/opencode","file_path":"","previous_cwd":"","task_id":"9","task_title":""} |
There was a problem hiding this comment.
This appears to be local Claude runtime state/artifact and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
| {"event":"task_completed","teammate":"","task_id":"7","subject":"Phase 3: Local deploy & firing verification","description":"Build the project, deploy locally, verify all new hooks fire correctly with actual plugin load log v","agent_id":"","agent_type":"","timestamp":"2026-04-06T11:10:19Z"} | ||
| {"event":"task_completed","teammate":"","task_id":"9","subject":"ビルド・発火検証・コミット・PR","description":"bun run build --single → wrapper 起動検証 → コミット → PR 作成 → マージ","agent_id":"","agent_type":"","timestamp":"2026-04-08T07:43:31Z"} |
There was a problem hiding this comment.
This appears to be local Claude runtime state/artifact and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
| @@ -0,0 +1 @@ | |||
| 1775663913 | |||
There was a problem hiding this comment.
This looks like a machine-local timestamp file and is likely not intended to be committed. Consider removing it from the repo and adding an ignore rule (e.g. **/.claude/) to prevent future accidental commits.
Add scripts/link-guardrails.sh that symlinks guardrails profile commands and agents into .opencode/ on every `bun install`. This ensures the desktop app and bare `opencode` CLI always see all commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
.gitattributesmerge=ours +scripts/upstream-sync.shfor automated selective mergeChanges
Phase 1: Mode Accessibility (Issue #148)
.opencode/opencode.jsonc: Merged duplicatehookskeys (guardrails.sh was silently lost)packages/guardrails/bin/opencode-guardrails: AddedOPENCODE_EXPERIMENTAL_PLAN_MODE=truepackages/guardrails/profile/plugins/guardrail.ts: plan→auto chain intool.execute.afterwith phase guard + try/catchpackage.json: Addeddev:guardrailsscriptPhase 2: Upstream Sync
.gitattributes:merge=oursfor fork-only paths (hook, memory, guardrails, notification, etc.)scripts/upstream-sync.sh: Automated selective merge scriptFORK.md: Fork documentationKey Finding
hooks/memory/guardrails were never in upstream — they are 100% fork-only. Upstream never "removed" them.
Review
bun turbo buildpass, typecheck 13/13 passTest plan
bun dev:guardrails→ TUI launches/auto,/plan,/review,/shipvisible in command list/plan→ plan agent → plan_exit → workflow_phase="implementing"./scripts/upstream-sync.shauto-protects fork pathsCloses #148
🤖 Generated with Claude Code