Skip to content

[FEATURE]: Silent Message Insertion API #3378

@malhashemi

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).

Metadata

Metadata

Assignees

Labels

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

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions