Skip to content

Project-level skills missing from available-skills in REST API sessions #25686

Closed as not planned

Description

@twhittock

Description

Project-level skills (from .opencode/skills/) are missing from the <available-skills> system prompt block in sessions created via the REST API (POST /session + POST /session/{id}/prompt_async). Only user-level skills (~/.config/opencode/skills/) appear. Calling use_skill("project-skill") returns "not found".

Interactive sessions (TUI/web) see all skills correctly.

OpenCode version

1.0.203+

Steps to reproduce

  1. Create a project with skills in .opencode/skills/
  2. Start opencode serve
  3. Create a session via REST API: POST /session with directory pointing to the project
  4. Send a prompt: POST /session/{id}/prompt_async
  5. Observe: <available-skills> only shows user-level skills
  6. use_skill("project-skill") returns "not found"

Root Cause

Skill.state and Skill.discovered use InstanceState, which is backed by Effect's ScopedCache. The ScopedCache.make constructor captures the Effect context at construction time and merges it into lookup calls via effect.updateContext (see effect/ScopedCache.ts:82-85):

self.lookup = (key: Key): Effect.Effect<A, E> =>
  effect.updateContext(
    options.lookup(key),
    (input) => Context.merge(context, input)
  )

When InstanceState.make runs during app initialization, InstanceRef is not in scope. The captured context therefore lacks InstanceRef, and all subsequent ScopedCache.get lookups merge this stale context — even when the calling fiber has InstanceRef provided via Effect.provideService(InstanceRef, ...).

The lookup's yield* context resolves (yield* InstanceRef) ?? Instance.current. Without InstanceRef, it falls back to Instance.current (Node ALS), which is empty after the REST API middleware returns its 204 response. The skill discovery then receives the wrong context, causing Config.directories() to skip the project's .opencode directory.

Evidence: The permission system correctly resolves all project skills (permissions include ops-* paths from .opencode/skills/) because permission evaluation runs in the calling fiber's context. But Skill.available() goes through ScopedCache.get() → lookup() in a scope without InstanceRef.

Fix

In InstanceState.get(), eagerly resolve InstanceContext via yield* context (where InstanceRef IS available in the calling fiber), then pipe Effect.provideService(InstanceRef, ctx) onto the ScopedCache.get call. This propagates the instance context into the ScopedCache lookup scope.

export const get = <A, E, R>(self: InstanceState<A, E, R>) =>
  Effect.gen(function* () {
    const ctx = yield* context
    return yield* ScopedCache.get(self.cache, ctx.directory).pipe(
      Effect.provideService(InstanceRef, ctx),
    )
  })

Distinction from similar issues

Activity

  1. github-actions commented on May 4, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • The issue does not use one of the required issue templates (Bug Report, Feature Request, or Question). Please resubmit using the Bug Report template, which requires a proper Description field.
    • The title uses a fix: prefix (typically reserved for pull requests). Bug report titles should be descriptive of the problem, not a proposed fix.

    Please edit this issue to address the above within 2 hours, or it will be automatically closed.


    This issue might be a duplicate of existing issues. Please check:

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. changed the title [-]fix: project-level skills missing in REST API sessions (ScopedCache lookup loses InstanceRef)[/-] [+]Project-level skills missing from available-skills in REST API sessions[/+] on May 4, 2026
  3. github-actions commented on May 4, 2026

    @github-actions
    Contributor

    Thanks for updating the issue! It now follows the contributing guidelines and has all the required information. The compliance flag has been removed.

  4. github-actions commented on Jul 4, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions