68 Commits
Author SHA1 Message Date
esengine 2a9743d790 feat(extension): split the session capability, and bound one queued prompt
session.read and session.submit are separate capabilities, so a mod that only
reads usage cannot put words in front of the model. One prompt a mod queues is
bounded by [mods] submit_bytes (8 KiB, 0 for none) and refused with a typed
identity that does not spend the per-turn count. The docs say the turn a queued
prompt starts is held to this session's own approvals and sandbox.
2026-10-18 04:01:29 -07:00
esengine 96f261b398 docs(mods): the receipt gap for a running mod, in the docs and in matching English 2026-10-18 04:01:28 -07:00
esengine 4af9d05744 docs(extension): keep the new prose and comments inside the repo's budgets 2026-10-18 04:01:28 -07:00
esengine f28360eadc feat(extension): a mod reads the session and queues a prompt, and settings bound what it may add 2026-10-18 04:01:28 -07:00
esengine 00d6b85d34 docs(mods): the mod host, its trust and what it serves 2026-10-18 00:35:40 -07:00
yhh c32b46c9fd fix(plugins): re-check the approved bytes before every publication attempt and keep the in-place swap off Windows 2026-10-09 04:28:55 -07:00
yhh 8aed0bc8ab Merge studio into feat/fix-11734-c 2026-10-09 01:53:14 -07:00
esengine 0da518ce84 fix(mcp): redact endpoint credentials fail-closed in every display, preview and export surface
One structural rule (secrets.RedactEndpoint / CredentialKey / RedactArgs)
replaces mcpsetup.RedactURL's query-only masking. Userinfo and fragments are
dropped or masked, query and fragment keys are decoded before classification,
unparseable endpoints render as <redacted>, and transport diagnostics keep
error identity while omitting peer-supplied prose.

Operational values (install, approval fingerprints, persistence) are untouched;
preview and plan responses add separate display fields that the desktop mirror
and confirmation UI render.

Rebased onto the launcher-aware NameFromArgv: names derive from the original
argv but never from a value the display projection masked, so build-flag values
such as `go run -exec` still resolve. Install classification reads the typed
HTTP status instead of sniffing the server name out of the message.

Fixes #11785
2026-10-03 19:19:58 -07:00
xiexian a4b5a9a485 docs(ecosystem): add compatible command example and host proof 2026-10-04 01:02:54 +08:00
xiexian 29791a36c6 docs(studio): add current theme author guide and starter 2026-10-04 00:04:23 +08:00
esengine 78069f638a fix(plugins): re-verify the approved bytes at publication and keep non-Windows replacement semantics 2026-10-03 02:22:21 -07:00
esengine f54da89d15 fix(plugins): bind the published plugin bytes to the approved preview and keep the publication failure typed 2026-10-03 00:31:46 -07:00
esengine 0b76ca0f63 fix(plugins): publish a replaced plugin directory without renaming a watched tree on Windows 2026-10-02 21:58:24 -07:00
YHH 3c1ad1153b Merge pull request #11584 from KHG420/codex/studio-mcp-author-example
feat(examples): add a runnable stdio MCP plugin
2026-10-02 13:23:31 -07:00
YHH ebc29fc2bc Merge pull request #11507 from KHG420/codex/studio-installed-hook-context
test(studio): verify installed hook context lifecycle
2026-10-02 13:04:51 -07:00
xiexian 4e76141a41 docs(plugins): add native prompt author example 2026-10-02 17:09:22 +08:00
YHH 51163562f1 Merge pull request #11681 from KHG420/codex/studio-plugin-guide-current-controls
docs(studio): align plugin guide with current controls
2026-10-02 01:28:37 -07:00
xiexian 0f48be263d docs(studio): align plugin guide with current controls 2026-10-02 15:37:32 +08:00
xiexian b0578fca34 fix(studio): report plugin theme validation warnings 2026-10-02 15:03:19 +08:00
xiexian e309ee2200 docs: add a packaged read-only subagent exercise 2026-10-02 13:39:32 +08:00
xiexian 29c033cfd5 docs(studio): add session-start context author example 2026-10-02 01:28:24 +08:00
xiexian 3f35e37388 feat(examples): add a runnable stdio MCP plugin 2026-10-01 19:14:56 +08:00
YHH 11b84e5891 Merge pull request #11481 from KHG420/codex/studio-frontend-page-example
docs(studio): add a local frontend delivery example
2026-09-30 20:28:10 -07:00
xiexian 37924b57e4 test(studio): verify installed hook context lifecycle 2026-10-01 11:09:51 +08:00
xiexian 03d5346869 docs(studio): add a local frontend delivery example 2026-10-01 08:15:01 +08:00
xiexian 00d9c2e490 docs(studio): add a source-linked API guide example 2026-10-01 07:48:20 +08:00
YHH f69e2c5798 Merge pull request #11472 from KHG420/codex/studio-issue-fix-example
docs(plugins): add a runnable repository issue exercise
2026-09-30 15:39:20 -07:00
YHH 3c0c265c22 Merge pull request #11467 from KHG420/codex/studio-compatible-mcp-effect
test(plugins): cover compatible MCP install lifecycle
2026-09-30 15:15:30 -07:00
xiexian 3b21d25de8 docs(plugins): add a runnable repository issue exercise 2026-10-01 05:42:27 +08:00
xiexian 1718074787 test(plugins): cover compatible MCP install lifecycle 2026-10-01 05:11:41 +08:00
xiexian 5c78af6b09 fix(studio): install git-subdir marketplace plugins 2026-10-01 04:09:53 +08:00
xiexian 2abcc7637e docs(studio): add multi-file skill package example 2026-09-29 22:51:14 +08:00
xiexian c5310be184 docs(studio): add tested community author workflow 2026-09-29 19:06:43 +08:00
yhh d318d24050 feat(extension): a plugin runtime can give the agent new tools
A runtime could only replace a tool by name, and that slot was never routed
to it, so a plugin that wanted to add one had to ship an MCP server. A
runtime now declares tools in its manifest, under runtime.tools with the
tools capability: a name, a description and an object input schema, known
before the process starts and shown in the install plan and the package
list. At initialize the runtime names the declared tools it serves; one the
manifest does not describe fails the handshake as capability_not_declared.

The model sees each as ext__<plugin>__<tool> and reaches it through the
capability catalog the way it reaches MCP tools, so installing or reloading
a plugin never moves the cached prompt prefix, and every call passes the
permission check under that name. A call is the new host request
extension/tool/call, bounded at five minutes; isError carries a failure the
tool reports about its own work, apart from a call that could not run.
A runtime that failed to start contributes no tools.

The Go SDK serves them from Options.Tools and names them in the handshake.
An effect test drives a call through the real assembly to a fake runtime and
back to the model, and fails with the registration removed.
2026-09-23 17:43:26 -07:00
yhh 2c7bde8710 feat(plugin): a package's prompts and themes reach Studio
Both were parsed by pluginpkg and went nowhere, while the docs said they
worked. A package's prompts are now discovered with its commands and invoked
the same way, /<plugin>:<name>, which is what the manifest documented. Its
themes are Studio theme packs: an entry names a pack's theme.json, and the
theme library lists it as plugin:<plugin>:<directory>, read in place from the
plugin root with its own images, never copied into the user's library, and
gone when the plugin is disabled. The retired desktop's .reasonix-theme
archives are not a format Studio reads, and the docs now say so.

The plugin docs are brought in line with the code: the Studio installer takes
a repository address or a folder (a .zip installs through the CLI), the routes
Studio drives replace a list of Wails methods that no longer exist, and the
protocol documents the panel and view surface kinds it already carries.
2026-09-23 17:23:35 -07:00
SivanCola 1c6776d9d9 fix(plugin): align Claude root instructions with upstream
Problem: Reasonix synthesized a SessionStart context hook from a Claude plugin root CLAUDE.md, although Claude Code does not load that file as plugin project context.

Root cause: Claude manifests shared the Codex compatibility helper that always appended root instructions.

Fix: keep supported Claude convention imports without the synthetic root hook, document the distinction, and update install-source fixtures to use official skills.

Verification: go test ./...; ./scripts/cache-guard.sh; focused plugin and install-source tests; desktop plugin/theme tests.
2026-08-10 01:59:34 +08:00
SivanCola bd97fecc5b fix(plugin): make native v2 resources explicit
Problem: Native v2 manifests could silently import Claude compatibility sidecars, including repository instructions and executable hooks.

Root cause: parseNativeV2 applied Claude compatibility discovery after validating the explicit manifest.

Fix: Treat the native v2 manifest as the complete capability boundary while preserving compatibility discovery for Codex and Claude manifests. Document the contract and cover it with a regression test.

Verification: go test ./...; cd desktop && go test . -run "Plugin|Theme" -count=1.
2026-08-10 00:08:02 +08:00
SivanCola 500d21a283 Fix runtime ownership and v2 manifest boundaries
Problem:
Runtime publish gates, receipts, file priors, message deduplication, and stream cancellation were process-global, so rebuilding one session could drain or overwrite another session's lifecycle evidence. The v2 manifest path also still exposed migration behavior for versioned manifests.

Root cause:
Lifecycle state was wired through package globals instead of the controller/session lineage, and migration/parser compatibility retained assumptions from the unpublished manifest v1 design. Drain cancellation also allowed callbacks to register after a generation had already expired.

Fix:
Introduce an explicit RuntimeOwner carried through boot, controller, provider, UI, host streams, receipts, and file-write compensation. Reuse it only within a rebuild lineage, defer replacement publication until commit, reject draining turns, make late drain registrations cancel immediately, and enforce v2-only versioned manifests while limiting explicit migration to pre-extension manifests without apiVersion.

Verification:
- go test -race ./internal/agent ./internal/boot ./internal/control ./internal/extension ./internal/extension/providerext ./internal/extension/uihub ./internal/tool/builtin -count=1
- go test ./internal/extension ./internal/pluginpkg ./internal/recovery ./internal/agent ./internal/control ./internal/boot -count=1
- go test ./... -count=1 (sdk/go)
- focused Desktop plugin lifecycle tests
2026-08-08 02:29:40 +08:00
SivanCola 697e32812e fix(hooks): normalize Windows POSIX scripts across hook sources
Problem: Windows could pass extensionless POSIX hook scripts from plugins, project settings, and global settings to cmd.exe, producing the not-an-internal-or-external-command warning. Claude implicit shell hooks could also retain Windows paths that Git Bash cannot resolve.

Root cause: hook execution mode and path normalization were applied only to explicit plugin conventions, while legacy and implicit shell sources were not inspected for POSIX shebang scripts.

Fix: detect POSIX shell shebangs across hook sources, route those scripts through the discovered Git Bash runtime, safely quote POSIX paths, normalize Claude plugin-root paths, and surface Bash requirements during runtime diagnostics.

Verification: go test ./internal/...; GOOS=windows GOARCH=amd64 go test -c ./internal/hook -o /tmp/reasonix-hook.test.exe; git diff --check.
2026-08-07 17:15:00 +08:00
SivanCola 4a2655185e feat(extension): resolve extension providers on first boot and in all model pickers
Follow-up to stage 7 closing the reviewer-flagged gap between the streaming
provider adapter and any production path.

Problem: sidecar providers merged into a resolver only after the controller
was built, and the merged resolver had no consumer — an extension provider
could not be the initial model, `default_model = "plugin/..."` failed with
unknown model, the CLI/Desktop model pickers enumerated only config
providers, and the stage-7 E2E bypassed the real resolution path.

Fix:
- Preflight extension runtimes before model resolution: sidecars start once
  via a single Manager (a build calls StartPackages exactly once), every
  pre-ownership error path closes it, and the provider merge (with
  provider:<ref> claims from the same manifest data the kernel uses) happens
  up front so conflicts fail before anything is built.
- The merged resolver feeds every resolution site: the model entry
  (plugin/-namespaced refs only; config models keep their full config entry,
  broker stays authoritative), the executor, the dual-model planner, the
  guardian, the recovery reviewer, and the subagent provider. Unknown-model
  errors list available plugin refs; RequireKey skips plugin refs (no config
  credential exists by design).
- Discovery and switching: Controller.ProviderCatalog() on the Capabilities
  port; the CLI picker, /model persistence (config.SetDefaultModel accepts
  plugin refs), Desktop ModelsForTab/SetModelForTab, and ACP
  session/new + SessionConfigState all enumerate and resolve plugin/... refs.
- Tests prove the real paths: first boot with default_model="plugin/..."
  streams the fake completion through Controller.RunTurn; unknown plugin ref
  wraps ErrUnknownModel and lists plugin refs; builtin->plugin switch via
  Rebuild streams from the sidecar with the old runtime alive until closed;
  required-preflight failure and unclaimed provider conflicts fail with the
  fake PID reaped; a failing new plugin on Rebuild leaves the old controller
  and sidecars untouched; CLI/Desktop/ACP discovery and switch coverage.
- Also fixes a merge-blocking AB-BA deadlock the branch exposed:
  tool.Registry.ContractEntries held the registry read lock across
  lazyTool.ReadOnly() (which takes the lazy-spawn mutex) while a lazy spawn
  swap held the mutex and wanted the registry write lock. Tool methods now
  run after the lock is released; output unchanged.

Verification: go test ./internal/boot ./internal/cli ./internal/acp
./internal/extension/... and cd desktop && go test . pass;
TestGoldenBaselineNoExtensions green unmodified; both protocol generators
-check green; ./scripts/cache-guard.sh 8/8 pass; go test -race on
internal/boot, internal/tool, and internal/extension/providerext pass;
gofmt/go vet clean; no new dependencies.
2026-08-04 05:49:39 +08:00
SivanCola 8ed68b3162 docs(extension): bilingual extension docs, SDK CI job, and final compatibility audit
Stage 10 of the unified extension kernel / Extension Protocol v1 plan.

- New docs: EXTENSIONS.md/.zh-CN.md (capability map, install/manage,
  fail-atomic reload semantics, development guide, compatibility,
  full-trust security model) and EXTENSION_PROTOCOL.md/.zh-CN.md (prose
  companion to the generated index: transport, lifecycle, content refs,
  interception, streaming providers, structured UI, errors, v1 stability
  contract, security model).
- Updated docs: PLUGIN_PACKAGES (Manifest v1 chapter: strict parsing,
  merge rules, prompts/themes, runtime declaration, FULL TRUST disclosure
  and --link trust inheritance), CAPABILITY_DIAGNOSTICS (additive v1
  prompts/themes/runtime fields, schema_version stays 1), CLI (/reload),
  ACP (_reasonix.io/session/reloadExtensions, extensionSurface
  negotiation, /<plugin>:<action> discovery), RELEASING (sdk/go/vX.Y.Z
  tag convention, three-product tag contract unchanged), THEME_PACK
  (plugin theme kind and fallback-preserve, from stage 4).
- CI: new sdk job on the three-OS matrix (gofmt, vet, stdlib-only guard,
  test, race on pushes) triggered by sdk/ or internal/extension/ changes;
  root conformance tests run in the existing root matrix.
- Makefile: sdk-test / sdk-test-race targets.
- Bundle budget: initial raw JS+CSS 2250 -> 2265 KiB with a comment
  recording the extension UI surface as the cause; every gzip budget
  (the user-visible transfer cost) is unchanged and passing.

Compatibility audit (kept green by tests added in stages 1-9): legacy
manifests parse byte-identically, plugin-packages.json schema unchanged,
session JSONL untouched with extension state isolated to
<session>.extensions.json, desktop theme state preserves plugin: IDs
across uninstall while legacy files load unchanged, install-plan JSON is
omitempty-additive, plugin/... refs degrade to model-unavailable on old
versions, and the two new event kinds append at the enum end with
text fallbacks for old clients.
2026-08-04 02:42:27 +08:00
SivanCola 84c6484edb Fix Windows plugin Bash hook runtime detection / 修复 Windows 插件 Bash Hook 运行时探测 (#6980)
* fix: honor configured Bash for Windows plugin hooks

Problem:
Windows plugin hooks that explicitly request Bash can fail after upgrade even when a usable non-standard Git Bash path is configured. Expanded plugin paths can also remain in a form that Git Bash cannot consume.

Root cause:
Hook execution and diagnostics rediscovered Bash independently instead of receiving the effective configured shell path, while Windows plugin-root expansion did not normalize shell-form commands for Bash.

Fix:
Propagate the configured Bash runtime through hook execution and diagnostics, normalize expanded Windows plugin paths for explicit Bash hooks, and report missing runtime dependencies in CLI and Desktop doctor surfaces. Add Superpowers and custom-path regression coverage plus bilingual documentation updates.

Verification:
go test ./internal/hook ./internal/capdiag ./internal/boot ./internal/cli
go test -race ./internal/hook ./internal/capdiag
env -u DEEPSEEK_API_KEY go test ./...
(cd desktop && go test ./...)
go vet ./internal/hook ./internal/capdiag ./internal/boot ./internal/cli
(cd desktop && go vet ./...)
Windows amd64 cross-compilation for affected root and Desktop packages
git diff --check

Cache-impact: none

* fix: remove obsolete Windows hook wrappers

Problem:
The CI unused-code linter rejects two compatibility wrappers that became unreachable after Hook execution started injecting the effective Bash runtime.

Root cause:
The runtime-aware call sites moved to resolver-injected helpers, but the old zero-argument resolver wrappers remained in the Windows compatibility module.

Fix:
Remove the two obsolete wrappers and keep the shared runtime-aware parsing helpers and cached Bash discovery path.

Verification:
go test ./internal/hook ./internal/capdiag ./internal/boot ./internal/cli
go vet ./internal/hook ./internal/capdiag ./internal/boot ./internal/cli
gofmt -l internal/hook/windows_compat.go
git diff --check
bash scripts/check-cache-impact.sh with the PR metadata

Cache-impact: none

* test: normalize Superpowers Bash hook expectation

Problem:
The Windows CI test expected a host-native backslash plugin root even though explicit Bash hooks intentionally receive a forward-slash path.

Root cause:
The Superpowers 6.2.0 regression assertion joined the temporary plugin root without applying the same platform-independent slash contract that the Hook loader guarantees for Bash.

Fix:
Normalize the expected plugin root with filepath.ToSlash so the test verifies the intended Git Bash command on Windows and remains stable on Unix hosts.

Verification:
go test ./internal/hook
Windows amd64 test-binary compilation for internal/hook
gofmt -l internal/hook/hook_test.go
git diff --check

Cache-impact: none
2026-07-27 20:29:08 +08:00
SivanCola 72a1e32b49 Fix cross-platform plugin Hook execution contracts / 修复跨平台插件 Hook 执行契约 (#6911)
* Fix plugin hook execution contracts

Problem: Windows plugin hooks with compound commands or nested quoting could still fail after the narrow quoted-batch compatibility fix, while explicit empty args could lose exec-form semantics across JSON round trips.

Root cause: Hook launch behavior was inferred from argument length and platform shell heuristics instead of preserving the manifest's exec-form or shell-form contract. CMD and PowerShell command strings also passed through incompatible quoting layers.

Fix: Preserve args field presence and shell selection through plugin parsing and loading, add explicit legacy/exec/shell execution modes, transport raw CMD and encoded PowerShell scripts without token re-rendering, retain the existing simple batch compatibility path, and document the cross-platform contract.

Verification: Focused Hook and plugin package tests, race and shuffled tests, fuzzing, root and Desktop full suites, go vet, and Windows/Linux cross-compilation.

* Fix Windows hook output regressions

Problem: The new compound CMD regression expected cmd.exe to strip quotes from %1, leaving the Windows job red. PowerShell hook scripts also preserved UTF-16LE input transport without forcing captured output to UTF-8.

Root cause: The test confused %1 with the dequoting %~1 form, and the Hook-specific EncodedCommand path did not reuse the shell runtime's existing PowerShell output-encoding prologue.

Fix: Align the CMD assertion with native batch semantics, share the PowerShell UTF-8 script wrapper across shell and Hook launchers, and exercise Chinese plus emoji output in the Windows runtime test.

Verification: Focused Hook/plugin/sandbox tests and vet; race/shuffle count 3; root and Desktop full suites; full vet; Windows amd64 test cross-compilation; PowerShell encoding fuzzing.
2026-07-24 23:07:09 +08:00
SivanCola 7e32590a68 fix(hooks): Windows plugin hook compatibility (#6550)
Problem:
On Windows, cmd.exe does not expand POSIX-style plugin-root variables or provide
`sh`, so plugin hooks received a literal ${REASONIX_PLUGIN_ROOT} path and explicit
`sh -c` / `bash -c` hooks failed before their scripts could run. Localized cmd.exe
errors were also surfaced to the UI as mojibake.

Root cause:
- expandPluginRoot only recognized ${CLAUDE_PLUGIN_ROOT}/$CLAUDE_PLUGIN_ROOT, and
  env expansion ran after host vars were set, so host-provided paths could be
  accidentally expanded.
- spawnCommand always went through cmd.exe on Windows, which cannot find a Git Bash
  installed outside its PATH.
- Hook stdout/stderr were surfaced as raw bytes, so legacy code-page (GB18030)
  output reached the renderer as mojibake.

Fix:
- Expand ${CLAUDE_PLUGIN_ROOT}/${REASONIX_PLUGIN_ROOT} plus their unbraced $NAME and
  Windows %NAME% spellings via a single-pass scanner that never re-expands the
  resolved root; expand plugin env before writing host vars.
- Route explicit bare `sh -c` / `bash -c` hooks on Windows through the shared
  hardened Git Bash discovery, preserve explicit interpreter paths, and return an
  actionable error when no usable Bash is found.
- Normalize legacy code-page hook output to valid UTF-8, preserving truncated
  multibyte prefixes and leaving UTF-8-native output byte-for-byte.

Verification:
- go test ./internal/hook ./internal/pluginpkg ./internal/config
- go test -race ./internal/hook
- go vet ./internal/hook ./internal/pluginpkg
- GOOS=windows GOARCH=amd64 go test -c ./internal/hook
- CI: all checks green (cache-impact, desktop-windows, test (windows-latest), race,
  CodeQL, govulncheck)

Cache-impact: none - only local plugin hook spawning and output decoding change;
provider-visible prompts, tool schemas, and ordering are unchanged.
2026-07-16 14:14:23 +08:00
SivanCola 0d5f70401b fix(plugins): polish hook adapter semantics and deflake spawn tests
- An unbounded Reasonix wait now omits TaskOutput's optional timeout
  instead of claiming 0 — in Claude's schema timeout is the maximum wait
  in ms, so 0 would read as "don't wait", the opposite of the call.
- Structural input gaps (WebFetch prompt, NotebookEdit cell_number,
  TaskOutput multi-job) are reported once per hooks file instead of once
  per hook item, so a wildcard-matcher plugin gets one warning per gap
  rather than one per hook; file-level wording also keeps the emitted
  set deterministic across the random event-map iteration order.
- Real-spawn hook tests share one generous realSpawnTimeout: node cold
  starts and first-run scans of a freshly built test binary can stall
  for seconds on a loaded machine, and these tests assert behavior, not
  latency. The timeout-path case keeps its tight budget on purpose.
2026-07-15 02:24:50 +08:00
SivanCola c0c93610e6 fix(plugins): complete Claude hook contract adapters 2026-07-15 01:33:36 +08:00
SivanCola ef7e813f2e fix: complete Claude hook tool_input/tool_response contract translation
Verified against the Claude Code 2.1.209 tool schemas and the official
security-guidance plugin source:

- Extend tool_input key renames beyond the four file tools: notebook_edit
  path->notebook_path, run_skill/read_only_skill name/arguments->skill/args,
  bash_output/kill_shell job_id->bash_id/shell_id, and the dedicated
  subagent wrappers' task->prompt, so Claude-authored guards reading those
  fields no longer fail open on empty values.
- Synthesize Agent's prompt for parallel_tasks from its sub-task prompts
  (keeping tasks alongside), bridging the structural mismatch so an
  Agent-scoped guard inspects all dispatched work.
- Resolve relative file_path/notebook_path absolute against the payload cwd
  (the same root Reasonix file tools resolve against), matching Claude's
  "must be absolute" file-tool contract for prefix-matching guards.
- Deliver Bash tool_response in Claude's {stdout, stderr, interrupted}
  shape instead of a bare string; the security-guidance plugin's
  commit/push checks read exactly these fields and treat a non-object
  response as empty, silently passing.

web_fetch needs no translation: its url field already matches Claude's.
2026-07-15 00:45:52 +08:00
SivanCola b0255bc0ec fix(plugins): translate tool_input field names and cover every Agent entry point
Fifth review round on #6434 found two more P1 gaps in the Claude hook
compat layer:

- marshalPayload forwarded Reasonix's tool-call arguments verbatim as
  tool_input. Reasonix's Read/Write/Edit/MultiEdit use a "path" key; Claude's
  own tool_input schema (confirmed against the current hooks reference docs)
  uses "file_path" for those same tools. A guard hook reading
  ".tool_input.file_path" therefore saw an empty value and could fail open
  even though its matcher correctly fired. Bash's "command" and Glob/Grep's
  "pattern"/"path" already match Claude's field names by convention, so only
  the file-path-taking tools needed a rename; NotebookEdit's cell_number
  (0-based index) vs Claude's cell_id (opaque identifier) is a deeper
  structural difference left untranslated rather than guessed at.
- Only "task" mapped to Claude's "Agent" tool. Reasonix has six more
  subagent-spawning entry points that reach PreToolUse under their own
  names: read_only_task, parallel_tasks, and the dedicated explore/research/
  review/security_review wrapper tools (BuiltinSubagentTools in
  internal/skill/tools.go — confirmed these are independently-registered
  tools, not calls routed through run_skill). A Claude plugin's "Agent"
  safety matcher silently missed all of them. All seven now map to "Agent"
  (with the legacy "Task" matcher alias preserved). Also mapped
  read_only_skill -> Skill, alongside run_skill, per the same audit.

Co-authored-by: SivanCola <[email protected]>
2026-07-14 09:55:10 +08:00
SivanCola 19e25a9259 fix(plugins): stop hook auto-allow from bypassing fresh-human approval
Third review round on #6434 found a real regression from the prior fix, plus
two remaining accuracy gaps:

- [P1, security] A Claude PermissionRequest hook's "allow" decision (added in
  the previous fix) was honored unconditionally, including for tools that
  RequiresFreshHumanApprovalTool deliberately excludes from YOLO/auto-approval
  and Guardian: sandbox escapes, Reasonix config writes, and memory
  remember/forget. A broadly-matched plugin hook returning allow could
  silently rubber-stamp any of those. Auto-allow is now ignored for
  fresh-human-required tools (falls through to the normal interactive
  prompt); a hook's deny still applies universally since refusing is always
  safe to auto-honor.
- [P1, security] The Claude subagent tool was renamed "Task" -> "Agent"
  (confirmed against the current https://code.claude.com/docs/en/tools-reference);
  Reasonix's "ask" tool (AskUserQuestion's equivalent) wasn't mapped at all.
  A matcher or tool_name check written against either current name silently
  never fired. Now emits the current name (task -> Agent, ask ->
  AskUserQuestion) while still matching hooks whose matcher uses the legacy
  "Task" alias.
- [P2] Documented, at both the code (compatibilityFor) and doc level, exactly
  what "full" compatibility does and doesn't guarantee: it reflects
  successful static mapping of declared capabilities, not full runtime
  parity — updatedInput and PreToolUse's ask/defer decisions are chosen by a
  hook's stdout at call time and can't be flagged during install the way
  if/asyncRewake/Stop/SubagentStop already are.

Co-authored-by: SivanCola <[email protected]>
2026-07-14 04:06:03 +08:00
SivanCola 741fb83664 fix(plugins): map Claude tool names in hooks and close remaining deny gaps
Second review round on #6434 found two more hook security gaps and an
over-claimed compatibility status:

- imported Claude hook matchers (e.g. "Bash", "Write|Edit") and the
  tool_name field a hook script inspects were left in Reasonix's own
  vocabulary (bash, write_file, ...), so a matcher-scoped or tool_name-gated
  Claude hook silently never fired. Added a name-translation layer
  (claudeFacingToolName) applied only to PayloadFormat "claude" hooks, both
  at match time and in the emitted payload.
- UserPromptSubmit's standard exit-0 JSON block (top-level
  {"decision":"block","reason":...}, distinct from PreToolUse/
  PermissionRequest's hookSpecificOutput shape) was not parsed, so a hook
  denying a sensitive prompt via JSON instead of exit 2 was ignored.
- PermissionRequest's "allow" decision (auto-approve without prompting) was
  never implemented — only the deny half of Claude's auto-answer contract
  from the prior fix. Runner.PermissionRequest now returns a tri-state
  decision (nil/deny/allow) and the approval flow honors all three.
- claude_compat.go now parses the "if" and "asyncRewake" hook fields and a
  Stop/SubagentStop hook import, and downgrades compatibility to "partial"
  with a structured warning instead of claiming "full" for semantics
  Reasonix doesn't evaluate (conditional execution, wake-on-exit-2 async,
  and block-the-turn respectively). The hook is still imported best-effort.

An existing test (TestPlanClaudeCompatibilityReportsAgentsHooksAndMCP,
modeled on the PR's own agent-critter example) had asserted "full"
compatibility for exactly this Stop-hook case; updated to the corrected
"partial" expectation.

Co-authored-by: SivanCola <[email protected]>
2026-07-14 03:38:21 +08:00