Skip to content

[FEATURE]: Silent Message Insertion API #3378

Closed

Description

@malhashemi

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Summary

Add ability to insert user messages into sessions without triggering AI inference. This enables plugins to inject context (like Anthropic's Agent Skills pattern) without generating AI responses.

Problem Statement

Current Limitation: The POST /session/{id}/message endpoint is architecturally coupled - it always triggers AI inference when creating a user message. There is no way to add context to a session without receiving an AssistantMessage response.

Impact on Plugins:

  • Cannot replicate Anthropic's Agent Skills pattern
  • Must use tool responses for context injection
  • Tool responses may be purged from context automatically
  • Unnecessary AI responses when only adding context

Current Workaround (from opencode-skills plugin):

async execute(args, toolCtx) {
  // Can only return string as tool response
  return `# ⚠️ SKILL EXECUTION INSTRUCTIONS ⚠️

**SKILL NAME:** ${skill.name}
**SKILL DIRECTORY:** ${skill.fullPath}/

## PATH RESOLUTION RULES:
All file paths mentioned below are relative to: ${skill.fullPath}/

---

${skill.content}
`
}

Problems with Current Approach:

  • ❌ Tool responses may be purged from context
  • ❌ More verbose than needed
  • ❌ Doesn't match Anthropic's clean pattern
  • ❌ No control over message persistence

Use Case: Anthropic Agent Skills Pattern

Anthropic's Claude Code uses a clean pattern for skill execution:

1. Tool call to skills_klorpify
2. User message: "The 'klorpify' skill is running"
3. User message: "Base directory: /path\n\nContent..."
4. Tool response: "Launching skill: klorpify"

Key Characteristics:

  • User messages inserted without triggering AI
  • Multiple user messages in sequence
  • Minimal tool response
  • Clean, persistent context injection

This pattern is currently impossible in OpenCode because:

  1. session.prompt() always triggers inference
  2. No API exists for silent message insertion
  3. Plugins can only return strings from tools
  4. No internal API access from plugin context

Proposed Solution

Add a skipInference parameter to the existing POST /session/{id}/message endpoint.

Option A: skipInference Parameter

API Change:

POST /session/{id}/message
{
  "skipInference": true,  // ← NEW optional parameter
  "parts": [{
    "type": "text",
    "text": "Context to inject without AI response"
  }]
}

Behavior When skipInference: true:

  • Creates and stores UserMessage
  • Publishes message.updated event
  • Does NOT call streamText()
  • Returns { message: UserMessage } (no AssistantMessage)

Plugin Usage Example:

async execute(args, toolCtx) {
  const client = createOpencodeClient()

  // Inject skill content as user message
  await client.session.prompt({
    path: { id: toolCtx.sessionID },
    body: {
      skipInference: true,
      parts: [{
        type: "text",
        text: `Base directory: ${skill.fullPath}\n\n${skill.content}`,
      }]
    }
  })

  // Return minimal tool response
  return `Launching skill: ${skill.name}`
}

Advantages:

  • ✅ Minimal code changes
  • ✅ Backward compatible (default: false)
  • ✅ Explicit and clear intent
  • ✅ Works with existing SDK clients
  • ✅ No new endpoint needed

Option B: New Endpoint (Alternative)

Create dedicated POST /session/{id}/context endpoint for context injection.

Advantages:

  • ✅ Cleaner separation of concerns
  • ✅ Explicit intent via endpoint name
  • ✅ Doesn't modify existing endpoint semantics

Disadvantages:

  • ❌ More code changes
  • ❌ New API surface to maintain
  • ❌ Requires SDK regeneration

Related Issues

Questions for Discussion

  1. How should injected messages be rendered in the UI (hidden, visible)?

I'm happy to implement this feature if the approach is approved. Please let me know if you'd like me to open a PR with Option A (recommended) or if you'd prefer Option B (new endpoint).

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Oct 23, 2025
  2. rekram1-node commented on Oct 24, 2025

    @rekram1-node
    Contributor

    how does this solve for tool call purges tho, thats a good point for this

  3. malhashemi commented on Oct 24, 2025

    @malhashemi
    ContributorAuthor

    It would allow plugins to insert a message into context (in my case it is the SKILL.md content) as a user message (Exactly how anthropic is doing it at the moment) currently I am returning the file content as a tool response which could get purged.

    If I simply use the current endpoint then it will trigger a new AI response. If the agent is already working it will be queued.

    I know the team is busy and I think I have an idea of a minimal approach to allow this behaviour but would like to get your blessings before pulling any work/PR.

  4. rekram1-node commented on Oct 24, 2025

    @rekram1-node
    Contributor

    ah okay i see

  5. rekram1-node commented on Oct 24, 2025

    @rekram1-node
    Contributor

    Yeah i think that's fine then you just need to add it to prompt input, add early return after create User message call, and then regenerate typescript sdk

    I don't love skipInference but other names don't come to mind

  6. gregid commented on Oct 24, 2025

    @gregid

    Nice addition.
    Some alternative names that come to mind:

    "silent": true,
    "background": true,
    "mode": "context" //with "message" or "prompt" being the default
  7. malhashemi commented on Oct 25, 2025

    @malhashemi
    ContributorAuthor

    @gregid thanks for your comment I think I will go with "mode": "context" with the default being "message", just in case someone might think of an addtional functionality in the future so they can simply expand with another mode. @rekram1-node what do you think?

  8. rekram1-node commented on Oct 25, 2025

    @rekram1-node
    Contributor

    @malhashemi we already have other notions of "mode" and so do other tools, I'd prefer something more explicit and distinct

  9. malhashemi commented on Oct 25, 2025

    @malhashemi
    ContributorAuthor

    @rekram1-node the only other option I can think of other than skipInference: true which explains the functionality technically would be contextOnly: true that one is focusing on the behaviour rather than what it technically does. Pick one :)

  10. rekram1-node commented on Oct 25, 2025

    @rekram1-node
    Contributor

    I think either of those work fine I will leave it up to you, I will say that I do kinda like noReply it is pretty explicit and simple (but like i said u can decide :))

  11. malhashemi commented on Oct 25, 2025

    @malhashemi
    ContributorAuthor

    noReply is actually nice I will go with that one! Thanks

  12. malhashemi commented on Oct 25, 2025

    @malhashemi
    ContributorAuthor

    @rekram1-node ready for your review, I have added tests as well let me know if you would like to drop these, feel free to edit as you see fit. Thanks

  13. malhashemi commented on Oct 26, 2025

    @malhashemi
    ContributorAuthor

    This is now resolved by #3433

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

Metadata

Metadata

Assignees

Labels

discussionUsed for feature requests, proposals, ideas, etc. Open discussion

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions