mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 17:57:54 -10:00
fix/opencode-session-header
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2479884dea |
feat(plugins): add zero plugins info command (#773)
* feat(plugins): add zero plugins info command Add plugins info to inspect manifest state, extension counts, and lockfile source/hash with optional hash drift detection. * fix(plugins): derive lock dir from plugin install path for info Remove LockDir from InfoOptions and read the lockfile from filepath.Dir(plugin.PluginDir) so lock metadata matches the resolved plugin location. Consolidate duplicate lock hash output lines. |
||
|
|
a880ce80a6 |
feat: gate project-scoped hooks, plugins, and MCP servers behind workspace trust (#529)
* feat(workspacetrust): add exact-match trust store for project config gating * feat(hooks): add ExcludeProject option to LoadConfig * feat(plugins): add ExcludeProject option to Load * feat(cli): gate project hooks and plugins behind workspace trust Trust check lives inside newHookDispatcherWithExtra and activatePlugins (fail-closed by construction); trustRoot is the original launch dir so a --worktree run inherits the source repo's trust. Callers OR the skip reports and emit one combined stderr notice. * feat(cli): add zero trust command (trust/list/remove) * fix(review): key spec-draft trust on the original launch dir; cover trust CLI edge branches Spec-draft runs (zero exec --use-spec --worktree) passed the reassigned worktree path as trustRoot, dropping a trusted repo's project hooks (fail-closed, but breaks worktree-inheritance). Thread the captured pre-worktree trustRoot into execSpecDraftRun. Add tests for the trust 'remove' too-many-args and --help branches, and a resolveTrust comment noting the per-chokepoint check must not be hoisted to callers. * test(cli): end-to-end trust-gate coverage through agent.Run and exec --worktree Drives the real agent loop with a fake tool-calling provider so a project beforeTool hook actually fires (or is gated away) through production code: gate blocks a tool call via agent.Run, and --worktree / --use-spec --worktree key trust on the original launch dir. All three are load-bearing (RED when the gate or the trustRoot threading regresses). * style(cli): gofmt struct alignment in exec_spec.go * fix(workspacetrust): compare stored trust entries literally, no symlink re-resolution External adversarial review (codex) found a false positive: IsTrusted re-normalized stored entries on every check (Abs+EvalSymlinks), so a trusted path later replaced by a symlink to attacker content re-resolved to the new target and matched. Stored entries are already canonical (normalized once at Trust time); compare them as literals and only normalize the incoming query. Add a regression test for the symlink-retarget case. * feat(mcp): gate project-scoped MCP servers behind workspace trust Closes the P0 an external adversarial review (codex) found: project ./.zero/config.json stdio MCP servers spawned before the hook/plugin gate, an ungated arbitrary-execution path that could also self-trust the workspace. Add ExcludeProject to config.ResolveMCP (skips the project layer) and thread a fail-closed trust check into every MCP spawn site (exec, TUI setup+refresh, mcp tools list, mcp check); reporting sites stay ungated. Verified the sole server-command exec (mcp/client.go) is reachable only through the now-gated RegisterTools paths. * test(cli): cover default-mode beforeTool firing through the trust gate Assert a project beforeTool hook fires in the default auto and ask permission modes (not only under unsafe), and is blocked when the workspace is untrusted, so the gate is verified in the mode a normal run actually uses. * docs(cli): note MCP servers in zero trust help text and doc comment The gate covers project hooks, plugins, and MCP servers, but the help string and runTrust doc comment listed only hooks and plugins. * test(cli): make workspace-trust tests portable on macOS and Windows setTrustConfigRoot set HOME on darwin, but config.UserConfigDir is XDG-first there (=$HOME/.config), so tests wrote trust.json and user hooks.json where the code never read them; set XDG_CONFIG_HOME on every non-Windows platform instead. writeMarkerHook shells out to /bin/sh, absent on Windows, so its trusted-case marker never appeared; skip it there like writeMarkerHookScript already does. TestRunTrustAddThenList compared `trust list` output (canonicalized via EvalSymlinks) against the raw cwd, which diverges on the Windows runner's 8.3 short names (RUNNER~1 -> runneradmin); compare the resolved path, and add TestRunTrustListShowsCanonicalPath to exercise that via a symlink on any platform. * feat(cli): surface the project MCP skip in the workspace-trust notice Project MCP servers were dropped silently in an untrusted workspace: the trust notice covered hooks and plugins but not MCP, and app.go discarded the resolveTrust error. registerMCPToolsForWorkspace now returns a trustSkip, gated on a new projectMCPConfigExists check plus the previously-dropped store-read error, and emitTrustNotice is variadic so the exec, spec-draft, interactive, and `mcp tools list` paths all fold in the MCP skip. The skip drives the notice only; the gate stays on resolveTrust/excludeProject, so a trusted workspace is unaffected and an untrusted one still fails closed. * fix(cli): print zero trust --help to stdout with a success exit zero trust -h/--help/help routed usage to stderr with the usage exit code, unlike every other subcommand (mcp, sandbox, skills, cron, ...) which prints help to stdout and returns success. Align it; the unknown-subcommand path still writes to stderr with the usage exit code. * fix(cli): gate oauth MCP login and surface mcp-check trust notice Two project-scoped MCP activation sites were left outside the workspace trust gate. zero mcp oauth login loaded a project-defined OAuth server from an untrusted workspace and ran the full discovery/registration/token-exchange flow against its configured URL. Gate resolveOAuthServer with resolveTrust so an untrusted clone's ./.zero/config.json OAuth server is dropped before login, and emit the same one-line trust notice the other spawn sites use. zero mcp check already gated the project layer but discarded the trust-check result and never emitted the notice, so a gated project server read as a bare "not configured". Emit the notice on the not-found branch. The three remaining ungated resolveMCPConfig(cwd, false) sites (backends doctor/snapshot and the extensions listing) are enumeration-only and stay ungated. * test(cli): adapt MCP startup-skip tests to two-arg resolveMCPConfig --------- Co-authored-by: Kevin Codex <[email protected]> |
||
|
|
b1ab01f53f |
Hooks management command + build & supply-chain hardening (#193)
* Bump toolchain to go1.26.4 to clear reachable stdlib CVEs go.mod pinned toolchain go1.24.13, whose bundled standard library has 9 govulncheck-reachable CVEs (crypto/tls KeyUpdate DoS, net/http2 loop, crypto/x509 x3, net, net/url, os, net/textproto). CI and release build via go-version-file: go.mod, so shipped binaries inherited them. Bumping the toolchain directive to go1.26.4 clears all nine — govulncheck ./... now reports 0 reachable (minimum that clears them is go1.25.11; go1.26.4 matches the team's local Go). The go directive stays at 1.24.2 (minimum language version). Verified: go build/vet/gofmt clean, full go test ./... green. * Add govulncheck CI gate and SHA-pin GitHub Actions - ci.yml: new 'security' job runs govulncheck as a hard gate (fails on a reachable vulnerability; passes now that the toolchain is bumped) plus an advisory deadcode step that surfaces dormant code without blocking. - Pin every GitHub Action to a full commit SHA (checkout, upload-artifact, github-script) with a version comment, across all four workflows — previously only setup-go was pinned, leaving the mutable v4/v7 tags as a supply-chain risk. * Add hooks management command (add/remove/enable/disable) Wires the previously-dormant hooks.ConfigStore (10 unreachable funcs -> 0) into the CLI: zero hooks add <id> --event <event> --command <cmd> [--name --description --matcher --arg --user --json] zero hooks remove|enable|disable <id> [--user --json] Writes the project hook config by default (<cwd>/.zero/hooks.json) or the user config with --user, reusing the store's locking + atomic writes + validation (normalizeDefinition). New hooks are enabled; state is managed via enable/disable. JSON output is secret-scrubbed via redaction. Mirrors the existing 'zero mcp add/remove/enable/disable' shape. Covered by add/remove/toggle round-trip, validation, unknown-event, and JSON-redaction tests. * Fix govulncheck CI job: pin GOTOOLCHAIN to the go.mod toolchain go run govulncheck@latest selected the toolchain from govulncheck's own go.mod (downgrading to go1.25.11), which then could not load our go1.26-requiring packages (fips140only_go1.26.go), failing the security job. Resolve GOTOOLCHAIN from go.mod's toolchain line so govulncheck and deadcode both run under go1.26.4. * Address review: harden CI checkout, pin tool versions, validate hook event CodeRabbit findings on #193: - ci.yml: persist-credentials: false on all checkout steps (smoke/performance/security) — these jobs run repo code and never push, so don't keep the token in git config. - ci.yml: pin [email protected] and [email protected].0 instead of @latest, so the security gate is deterministic and not subject to supply-chain drift. - hooks: add exported IsValidEvent/KnownEvents (single source); parseEvent reuses it, and 'zero hooks add' now rejects an invalid --event with a usage error at parse time instead of falling through to the Upsert app-error crash path. |
||
|
|
250bf24c9d |
feat: add MCP manager UX (#188)
* feat: add MCP manager UX * fix: harden MCP manager UX * test: stabilize agent eval timeout on Windows * fix: preserve MCP server metadata * fix: clarify empty MCP view * feat: make MCP command a manager view * fix: make MCP manager usable in TUI * fix: make MCP manager selectable in TUI * fix: simplify MCP manager popup * feat: add searchable MCP marketplace * fix: address MCP review issues * fix: address MCP follow-up review * feat: add MCP setup wizard * feat: detect MCP setup requests in chat * fix: address MCP manager review feedback * fix: avoid hijacking MCP chat prompts * fix: persist MCP view cache fallback |
||
|
|
5e68c78824 |
wedge stage 10: distribution (skill/plugin install, tools make) (#184)
* stage 10: distribution (skill/plugin install, tools make)
Add a file-backed distribution surface so skills and plugins can be
installed from a git URL or local path, and new plugin-tools can be
scaffolded. No central registry: a source is just a git URL or path.
- internal/skills/install.go: Install/Remove/Info + skills.lock. Validates
SKILL.md frontmatter via the existing parser, copies it into the skills
dir, and records a sha256 content hash. Reinstall surfaces the hash
change; a clash with a different source is refused without --force.
- internal/plugins/install.go: same flow validated against the plugin
manifest schema, copying the whole plugin tree (minus .git) so the
plugin is activatable. plugins.lock records source + hash.
- internal/tools/scaffold.go: generate a prompt-gated plugin-tool skeleton
(manifest + runnable shell/node/python entry stub) in the toolbox dir.
- CLI: zero skill {add,info,remove}, zero plugin {add,remove}, and a new
zero tools {make,list}; wired in app.go/extensions.go/skills.go.
Safety: fetch honors the process environment (proxy/egress) and disables
git credential prompts; install never executes fetched content (skills are
markdown; plugin install scripts are never run) and installed plugins still
go through normal activation with permission gating.
* stage 10: make no-usable-name install test cross-platform
The test used a whitespace-only source dir name to force the no-usable-name rejection, which is not creatable on Windows (Smoke failure). Switched to a dir base that validSkillName rejects on every platform (contains "..") while staying creatable on Windows; the blank frontmatter still falls back to the unusable dir base, exercising the same rejection.
* stage 10: address review (tree hash, tools-list diagnostics, --json guards)
* stage 10: make canonical-source tests cross-platform (Windows)
Two Windows Smoke failures from the canonical-source review fix:
- TestInstallSameLocalSourceDifferentSpellingIsNotAClash (skills + plugins) used filepath.Rel(cwd, tempdir), which errors on Windows when the temp dir (C:) and repo (D:) are on different drives. Switched to a same-directory alternate spelling (redundant /./), which still exercises canonicalSource without a cross-drive relative path.
- TestRunSkillAddInfoRemove asserted the raw source path, but canonicalSource resolves symlinks/8.3-short-paths, so the stored+displayed source differs on Windows. Normalize the source via EvalSymlinks up front so the assertion matches on every platform.
* stage 10: address CodeRabbit nitpicks (variadic append, header comment)
|
||
|
|
59c332638d |
wedge M2: MCP server resources/prompts + sandbox scoped egress + MCP OAuth (#179)
* wedge stage 06: MCP server resources + prompts
Advertise resources and prompts capabilities in the initialize response and
handle resources/list, resources/read, prompts/list, and prompts/get alongside
the existing tools methods.
Resources enumerate in-scope workspace files via the shared workspace scanner
(reusing its gitignore-style exclusions) and read them read-only with strict
scope enforcement: traversal and out-of-scope absolute paths are rejected with
a JSON-RPC error and no contents, and binary files come back as base64 blobs.
Prompts expose a curated set of templates with simple {{arg}} substitution;
unknown prompt names return an error.
* wedge stage 07: sandbox scoped egress + auto-allow-bash
Add a middle-ground network mode between full allow and full deny:
- types.go: NetworkScoped network mode; Policy.AllowedDomains /
Policy.DeniedDomains; Policy.AutoAllowBashWhenSandboxed plus its
ZERO_SANDBOX_AUTO_ALLOW_BASH env surface (off by default).
- egress.go: in-process loopback (127.0.0.1:0) filtering proxy handling
HTTP CONNECT (HTTPS tunnel) and plain HTTP. Allows only AllowedDomains
minus DeniedDomains via exact-or-subdomain matching (github.com matches
api.github.com but not notgithub.com or github.com.evil.com); deny wins.
Denied targets get 403 / refused CONNECT. Decisions are logged through
the repo redaction so query tokens never hit the audit trail. Fails
closed: empty allowlist is rejected at construction.
- runner.go: NetworkScoped starts the proxy and routes the sandboxed
process through it (HTTP(S)_PROXY/ALL_PROXY + NO_PROXY into the sandbox
env; Linux keeps --unshare-net; macOS profile denies general network
but permits localhost:proxyPort). Empty-allowlist scoped collapses to
deny (effectiveNetwork). Any proxy-start error denies the build rather
than falling back to open network. NetworkAllow/NetworkDeny unchanged.
- engine.go / registry.go / bash.go: honor AutoAllowBashWhenSandboxed -
auto-allow bash without a prompt only when a wrapping sandbox is active
for the command; ignore the flag when the sandbox is inactive. Preflight
network gate now uses effectiveNetwork so a misconfigured scoped policy
still fails closed. Bash defers plan.Cleanup() to shut the proxy down.
Default behaviour is unchanged for existing NetworkAllow / NetworkDeny
users and for the off-by-default auto-allow flag.
* wedge stage 08: MCP OAuth client
Add an OAuth 2.0 + PKCE authorization-code flow so ZERO can connect to
remote MCP servers that require OAuth.
- internal/mcp/oauth.go: authorization-server metadata discovery with
config fallback/override, S256 PKCE, loopback redirect listener, state
(CSRF) validation, code+verifier token exchange, refresh, and optional
dynamic client registration. Tokens are never logged.
- internal/mcp/oauth_store.go: per-server token persistence in a 0600
JSON file under the config dir, with a redaction-safe status summary
that omits the token material.
- internal/mcp/network_client.go: OAuth-configured servers get a round
tripper that attaches the bearer token and, on 401, refreshes once and
retries; refresh failure surfaces an actionable re-login message.
- internal/mcp/config.go + internal/config: an MCP server entry may
declare auth: "oauth" with optional client registration / explicit
endpoints. Non-OAuth servers are unaffected.
- zero mcp oauth {login,logout,status} wired into the CLI surface;
status reports presence/expiry, never the token.
* wedge M2: address review — 2 security fixes, portability, robustness (#179)
Security (Critical):
- resources: resolve symlinks BEFORE the scope check (and resolve roots) so an
in-workspace symlink to an out-of-scope file (e.g. link -> /etc/passwd) is no
longer readable via resources/read. resources/read echoes the requested URI
(not the resolved path) so list/read stay consistent.
- sandbox egress: authorize the dialed URL host, not the client Host header, so
'GET http://denied/ ... Host: allowed' can't bypass the allowlist.
Robustness/correctness:
- prompts: reject prompts/get that omits a required argument; tokenized
substitution so {{...}} inside supplied values is preserved (not stripped).
- resources: percent-encode/decode file URIs via net/url (spaces, #, %).
- oauth: one timeout-scoped context bounds discovery + registration + callback +
exchange; joinWellKnown preserves issuer path segments (RFC 8414).
- cli: 'mcp oauth login' rejects --json instead of silently ignoring it; the
login test is bounded so a missed callback fails fast instead of hanging.
Portability: XDG token-store path test uses t.TempDir() so it passes on Windows
(fixes the Windows Smoke failure).
Adds regression tests for the symlink escape, Host-header bypass, required-arg
rejection, and placeholder preservation. Full go test ./... + -race green.
* wedge M2: fail closed on unenforceable scoped egress (review #179)
Addresses the two P1 scoped-egress enforcement gaps from review:
1. Policy-only fallback ran NetworkScoped with unrestricted networking. On Windows / any host without a native backend, 'network: scoped' with allowedDomains silently got open host networking because Evaluate permitted network-risk tools (expecting a proxy) while BuildCommandPlan ran a direct plan with no proxy. Evaluate now collapses scoped to deny when the active backend cannot enforce egress, so network-risk tools fail closed.
2. Bubblewrap 'scoped egress' could not reach the proxy. --unshare-net isolates the netns and there is no bridge to the host-loopback proxy (the comments claimed one; no code added it), so allowlisted egress never worked. Bubblewrap no longer starts an unreachable proxy: scoped collapses to deny (--unshare-net, no proxy env), matching Evaluate. Comments corrected; a real relay (e.g. slirp4netns) is left as future work.
Mechanism: new Backend.ScopedEgress capability (true only for sandbox-exec, which shares the host net under a seatbelt profile restricting outbound to the proxy port) + Backend.EnforcesScopedEgress(). sandbox-exec scoped egress is unchanged. Regression tests cover enforcing vs non-enforcing backends in Evaluate and the bubblewrap fail-closed plan.
|
||
|
|
6fb0231df8 |
TUI scrollback re-architecture, Lime chat surface, and deep-audit fixes (#158)
* docs(design): add Lime TUI prototype and rebuild spec
* tui: remove the zeroline skin system
One design remains in the tree ahead of the Lime rebuild: the zeroline
package, its TUI view/skin plumbing (Options.Skin/ThemeVariant/ThemeDark,
model skin branches, theme picker), and the zeroline CLI subcommand are
gone. /theme keeps its shell-only message.
* tui: replace the cyan palette with the Lime token table
theme.go is rebuilt around the Lime design tokens (docs/design/
zero_tui_lime.html): near-black panels, one lime accent, solid tint
stand-ins for the prototype's rgba overlays. Existing render sites map
onto the new role styles (ink/line/userPrompt/toolName/toolArg) without
layout changes; no hex exists outside theme.go.
* tui: build the Lime chat-surface components
Title bar with brand badge + context window, empty state replacing the
deleted splash (block-art 0, tagline, 1-3 starter suggestions), stream
blocks (user prompt gutter, muted interim with cursor, final-answer
accent rail, done line, bordered notes), tool cards with diff/read/
bash/grep bodies and MiniDot run spinner, borderless composer with
run-state hints, and the grouped status line. Final answers are marked
at append time; resolved tool calls collapse into their result cards.
* tui: fix Stage 2 review findings
Adversarial review of the chat-surface commit confirmed 15 issues, all
fixed here:
- wrapPlainText sliced wide runes by rune count: CJK/emoji prose could
panic the TUI or emit lines ~2x the measure; split at width instead
- a cancelled run's streamingText leaked into the next turn's interim
block; cancelRun now clears it
- orphaned tool-call cards (cancelled/errored turns) re-animated with
the spinner on any later run; the glyph is now scoped to the call's
own runID
- starter-suggestion digit capture was active while the chips were off
screen (/clear mid-run); the guard now mirrors the render condition
- prompt submits while a deferred Ctrl+C quit is draining could start a
run the quit would orphan; submission is blocked while exiting
- the running placeholder advertised ctrl+c (which quits) as the
interrupt key; it now says esc
- read-card parser expected 'N: text' but read_file emits 'N | text',
so the gutter and L-range never rendered on real output
- toolCard borders were one column short of the body rows
- diff tint bands now span the full row; NEW FILE renders green on
addBg; preamble/no-newline lines are unnumbered meta and no longer
shift hunk numbering
- card bodies and system notes paint the panel surface; the head gains
the separate faintest arg column (grep target + pattern)
- title-bar overflow now truncates the minimal candidate instead of
rendering the deleted design's 'ZERO … READY' line
- untracked the stray snake-game.html (left on disk, ignored)
* tui: restyle the interactive surfaces for Lime
Permission prompts render as the amber permission card (PERMISSION
badge, risk readout, key chips); resolved prompts collapse to one line
(allowed once/always/denied · tool) and unprompted allows fold into the
tool card's [auto] tag. Ask-user and spec-review prompts share the card
language with line borders. Autocomplete and picker overlays move onto
the panel/selection tints with the accent ❯ marker; model-picker rows
gain the provider dot and ctx · KEY_ENV metadata from the registry and
provider catalog. /resume renders its list as stacked session cards
(id + age, title, meta). Key handling is untouched everywhere.
* tui: adaptive width tiers, doc updates, final polish
Width tiers re-evaluate from the live width: ≥100 renders the full
spec; 80–99 drops the tool-arg column, header ctx, and the status
'interactive' group; 58–79 drops the diff/read gutters, bares the
badge, and keeps provider+tokens+mode; <58 renders a single-segment
header, rail-less cards, and a mode-only status line. Table-driven
tests cover {58,70,80,100,120} plus a frame-wide invariant that no
emitted line exceeds the terminal at any tier. Card bodies expand tabs
so width math holds. Dead splash-chip machinery (startupCommandNames,
startupOrder) is gone; README's TUI section now describes Lime.
* tui: fix final-review findings across Stages 3-4
A second adversarial review confirmed 14 issues, all addressed:
- permission collapse and tool-card pairing are now run-scoped (rcKey):
providers with repeating synthetic ToolCallIDs (Gemini gemini_tool_N)
could attribute a decision or result to a different run's call
- rehydrated always-grants no longer mislabel as 'allowed once'
(GrantMatched fallback)
- the width invariant now actually holds everywhere: empty-state
tagline/hint, ask-user rows, permission detail blobs, pending image
chips, and the sessions trailing hint are wrapped or fitted; the
invariant test renders the empty state and all of those rows at
widths 24-120
- every card (permission, ask-user, spec-review, sessions, notes)
drops side borders on tiny terminals, not just tool cards
- /resume card fields are sanitized against separator bytes from
user-controlled titles
- the picker's selected row paints its gap so the selBg band is solid
- dead splash helpers (normalizedStartupWidth, countLines, nonEmpty,
unreachable rowWelcome branch) and stale /theme comments are gone
- README no longer claims transcript scrolling keys or Tab-accept
behavior that never existed; chatWidth documents its 24-col floor
Tag tui-lime-s4 moves to this commit.
* audit: deep-audit report, TUI scrollback re-architecture, and cross-module fixes
TUI: settled-row flush frontier prints history to native terminal
scrollback (tea.Println, ordered + ack-serialized); View() renders only
the live tail. Full diffs (400-line flush cap) and OSC 8 file:// links
land in history; streamed interim text, cancellation markers, ask_user
exchanges, and composer input history are preserved; run-scoped dedup
keys fix Gemini's repeating tool-call ids; /exit, Ctrl+C-after-Esc, and
cross-session late flushes no longer orphan or contaminate checkpoints.
Cross-module: zero search panic + offset mapping, usage report
robustness, 529 retry, SSE keep-alive watchdog, registry corrections,
update --check on dev builds, MCP ping + mcp list --json secret
redaction, sandbox.network config knob, doctor apiKey presence, atomic
config writes, C1-safe notifications, rune-safe truncation everywhere,
gofmt + vet CI gates, dead glamour dependency dropped.
Full audit report: docs/audit/2026-06-10-deep-audit.md (175 findings).
Co-Authored-By: Claude Fable 5 <[email protected]>
* tui: hide malformed ask_user tool errors
* fix tui malformed tool retries
* fix max-turn finalization
* upgrade tui agent guardrails
* fix coderabbit guardrail findings
---------
Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: Vasanthdev2004 <[email protected]>
|
||
|
|
43c256068a |
feat: make Go runtime the app path (#71)
* feat: make go runtime the app path * fix: harden update semver parsing |
||
|
|
4e19bd992f | fix: address MCP review feedback | ||
|
|
e8e77c8a8f | [codex] expose MCP tools in Go CLI | ||
|
|
f43db6f600 |
[codex] add Go extension backend commands (#61)
* feat(go): add plugin manifest backend * feat(go): add extension policy stores * feat(go): expose extension backend commands * fix(go): reject rooted plugin paths cross-platform * fix(go): address extension backend review * fix(go): harden extension backend persistence * fix(go): preserve extension usage and hook enabled semantics * fix(go): use process-released MCP permission locks * fix(plugins): close symlink missing-leaf escapes * fix(plugins): fail closed on manifest realpath errors * fix(tests): keep plugin realpath regression portable * fix: enable CodeRabbit verdict workflow --------- Co-authored-by: KRATOS <[email protected]> |