Skip to content

fix(plugin): run awaited plugin ctx.ask + ctx.metadata through EffectBridge#1127

Merged
Astro-Han merged 1 commit into
devfrom
claude/l4-28217-plugin-ask-bridge
Jun 3, 2026
Merged

fix(plugin): run awaited plugin ctx.ask + ctx.metadata through EffectBridge#1127
Astro-Han merged 1 commit into
devfrom
claude/l4-28217-plugin-ask-bridge

Conversation

@Astro-Han
Copy link
Copy Markdown
Owner

@Astro-Han Astro-Han commented Jun 3, 2026

What

Plugin tools declare ctx.ask as Promise<void> and ctx.metadata as void, but the framework versions are Effects. The fromPlugin bridge spread toolCtx straight through (...toolCtx), so the returned Effects were never executed:

  • await ctx.ask(...) resolved an inert Effect object — the permission prompt was a silent no-op and the tool proceeded as if every request had been granted.
  • ctx.metadata(...) discarded its Effect — the tool's title/metadata was never applied.

Both are bridged through EffectBridge.make().promise(): ask is awaited; metadata is fire-and-forget (matching its void contract) with a log.warn on failure. The plugin ToolContext ask contract becomes Promise<void> (dropping the now-unused Effect import); metadata was already typed void.

Why

Semantic port of anomalyco/opencode#28217. PawWork has the same fromPlugin shape, so the same silent-no-op bug applies. The ctx.metadata half was caught in review (gemini-code-assist) — same file, same bug class, same fix.

Boundary

Three files, all within the plugin/tool surface:

  • packages/opencode/src/tool/registry.ts — bridge the plugin ask + metadata
  • packages/plugin/src/tool.tsask contract Effect.Effect<void>Promise<void>
  • packages/opencode/test/tool/registry.test.ts — regression test

Verification

  • bun test test/tool/registry.test.ts — 23 pass. New test red→green confirmed for both: with the un-bridged spread, each framework Effect runs 0 times (silent no-op); with the bridge, each runs exactly once (ask awaited, metadata fire-and-forget then flushed).
  • bun run typecheck — clean in both packages/opencode and packages/plugin.
  • Built-in tools (packages/opencode/src/tool/*.ts) use the framework Tool.Context (yield* ctx.ask/ctx.metadata), not the plugin ToolContext, so they are unaffected.

Risk

Low. Only the plugin-tool execution path changes; the bridge provides full Effect context, so the permission service and instance are intact when the bridged Effects run.

@Astro-Han Astro-Han added the bug Something isn't working label Jun 3, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 minute and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fc575aa1-8003-4750-8299-95320d77c109

📥 Commits

Reviewing files that changed from the base of the PR and between 56652b9 and b119c55.

📒 Files selected for processing (3)
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/plugin/src/tool.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/l4-28217-plugin-ask-bridge

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Astro-Han Astro-Han added P2 Medium priority upstream Tracked upstream or vendor behavior harness Model harness, prompts, tool descriptions, and session mechanics labels Jun 3, 2026
Copy link
Copy Markdown

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

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 changes the plugin tool context's ask method signature from returning an Effect to returning a Promise, and introduces EffectBridge in the tool registry to bridge the framework's Effect-based ask implementation. A corresponding test has been added to verify that the bridged permission request actually runs. The reviewer pointed out that toolCtx.metadata is also an Effect-returning function in the framework but is typed as returning void in the plugin context, which makes it a silent no-op. They suggested bridging metadata using bridge.promise as well to ensure it executes correctly.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/opencode/src/tool/registry.ts
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@Astro-Han Astro-Han force-pushed the claude/l4-28217-plugin-ask-bridge branch from 340320d to 08c7c7e Compare June 3, 2026 09:15
…Bridge

Plugin tools declare ctx.ask as Promise<void> and ctx.metadata as void, but
the framework versions are Effects. The fromPlugin bridge spread toolCtx
straight through, so the returned Effects were never executed: an awaited
ctx.ask(...) resolved an inert Effect (permission prompt a silent no-op, tool
ran as if granted) and ctx.metadata(...) discarded its Effect (title/metadata
never applied).

Bridge both through EffectBridge.make().promise(): ask is awaited, metadata is
fire-and-forget (void contract) with a warn on failure. Update the plugin
ToolContext ask contract to Promise<void> (dropping the now-unused Effect
import); metadata was already typed void.

Regression test: a .opencode/tool plugin whose execute awaits ctx.ask and calls
ctx.metadata now observes both framework Effects run exactly once (0 before the
bridge, 1 after).

Semantic port of anomalyco/opencode#28217 (ctx.metadata gap caught in review).
@Astro-Han Astro-Han force-pushed the claude/l4-28217-plugin-ask-bridge branch from 08c7c7e to b119c55 Compare June 3, 2026 09:41
@Astro-Han Astro-Han changed the title fix(plugin): run awaited plugin ctx.ask through EffectBridge fix(plugin): run awaited plugin ctx.ask + ctx.metadata through EffectBridge Jun 3, 2026
@Astro-Han
Copy link
Copy Markdown
Owner Author

Review notes

Codex review (xhigh): PASS — one P2 advisory, no P1.

Two judgment calls relative to the raw upstream port (anomalyco/opencode@12ae22378f):

  1. metadata bridged too — beyond upstream. Upstream bridges only ask. But upstream's plugin metadata is also typed void while the framework metadata is an Effect, so upstream has the identical silent no-op on metadata and simply missed it (ask-focused PR). gemini-code-assist flagged it here (HIGH); verified real via red→green (framework Effect runs 0× un-bridged, 1× bridged). Bridged fire-and-forget to match the void contract, with log.warn on failure. Kept — more correct value than bug-for-bug parity, same code block, low risk.

  2. Codex P2 (deferred, documented): bridged ask not tied to toolCtx.abort. bridge.promise runs the ask in a detached runtime, so cancelling a turn while a plugin permission prompt is pending won't run the ask's interruption cleanup → a stale pending prompt could linger. Upstream's reference bridge has the identical behavior (also abort-naive); it's a P2 advisory, and the metadata half is cosmetic fire-and-forget. Clean wiring would require out-of-boundary effect/bridge.ts changes or non-trivial race/interrupt plumbing beyond the smallest faithful port, so it's left as a follow-up candidate rather than expanding this PR.

@Astro-Han Astro-Han merged commit 7bc93ce into dev Jun 3, 2026
35 checks passed
@Astro-Han Astro-Han deleted the claude/l4-28217-plugin-ask-bridge branch June 3, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority upstream Tracked upstream or vendor behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant