So, if a new issue created, assigned to claude. Then calude agent will start to work on it.
While claude agent is running, but user adds a new comment into the issue or modify the issue itself. Possible to inject the new message (messages) / issue changes to the running claude agent?
So, if a new issue created, assigned to claude. Then calude agent will start to work on it.
While claude agent is running, but user adds a new comment into the issue or modify the issue itself. Possible to inject the new message (messages) / issue changes to the running claude agent?
Analysis: Injecting Messages to Running Claude Agent
I've analyzed the current implementation and the Claude Agent SDK capabilities. Here's what I found:
Current Implementation
The current system works as follows:
Webhook Event → Job Queue: When an issue is created/modified or a comment is added, the webhook server receives the event and adds it to a persistent job queue (src/jobQueue.js)
Sequential Processing: Jobs are processed one at a time (FIFO - first in, first out)
Single Execution: Each job creates a new Claude Agent SDK session that runs independently until completion
No Session Sharing: There's currently no mechanism to inject new information into an already-running Claude agent session
Key Code Location: client/src/claude-client.ts - The handleIssue() method creates a new query session for each job:
const queryInstance = query({
prompt,
options: { cwd, mcpServers, allowedTools, ... }
});
for await (const message of queryInstance) {
// Process messages until completion
}
Is Mid-Run Injection Possible?
YES, but it requires significant architecture changes. The Claude Agent SDK provides several capabilities that could enable this:
1. Session Resume & Fork ✅
What it does: Allows resuming a previous session with new instructions
How: Capture the sessionId from the initial response, then use the resume parameter:
// Initial query
const response = query({ prompt: "Work on issue #20" });
for await (const message of response) {
if (message.type === 'system' && message.subtype === 'init') {
sessionId = message.session_id; // Save this!
}
}
// Later: Resume with new context
const resumed = query({
prompt: "New comment: User says X",
options: { resume: sessionId }
});
2. Interrupt Method ⚠️
What it does: Stops a running agent mid-execution
How: Call await client.interrupt() on the SDK client instance
Limitation: Requires maintaining a reference to the client instance, which our current implementation doesn't do
3. Fork Session 🔀
What it does: Creates a branch from an existing session without modifying the original
How: Use forkSession: true with the resume parameter
Use Case: Explore alternative approaches from the same starting point
Implementation Challenges
To implement mid-run message injection, we would need to:
Challenge 1: Long-Running Process Management
Current: Each job creates a new Node.js process via handle-issue-ts.sh → client/dist/index.js
Problem: No way to communicate with an already-running process
Solution Options:
Option A: Convert to a persistent daemon that maintains active sessions
Option B: Use IPC (Inter-Process Communication) to send messages to running processes
Option C: Store session IDs and queue resume operations when new events arrive
Challenge 2: Session State Persistence
Need to store sessionId for each running/paused issue
Track which issues have active sessions
Determine when to resume vs. fork vs. create new session
Challenge 3: Queue Coordination
Current queue processes one job at a time
Mid-run injection would need to:
Detect if issue #X already has a running session
Either interrupt it and resume with new context, OR
Queue the new message for the next resume cycle
Challenge 4: SDK API Limitations
The SDK interrupt() method requires holding a reference to the client instance
Our current implementation doesn't expose the client between invocations
Would need to refactor to maintain persistent client instances
Proposed Architecture (High-Level)
If we want to support mid-run injection, here's a possible approach:
Architecture: Session Manager Service
┌─────────────────────────────────────────────────┐
│ Webhook Server (existing) │
│ - Receives events from Gogs │
│ - Enqueues jobs to JobQueue │
└──────────────────┬──────────────────────────────┘
│
v
┌─────────────────────────────────────────────────┐
│ Session Manager (NEW) │
│ - Maintains active Claude Agent sessions │
│ - Maps issue_number → sessionId │
│ - Handles: start, resume, interrupt, fork │
└──────────────────┬──────────────────────────────┘
│
v
┌─────────────────────────────────────────────────┐
│ Job Queue (modified) │
│ - Checks if session exists for issue │
│ - Routes to SessionManager for active sessions │
│ - Creates new session for new issues │
└─────────────────────────────────────────────────┘
Key Components
SessionManager Class
Maintains a Map<issueNumber, { sessionId, client, status }>
Before creating new job, check if SessionManager.hasActiveSession(issueNumber)
If yes: call SessionManager.resumeSession(issueNumber, newPrompt)
If no: create new session via SessionManager.startSession()
Event Deduplication
When new comment arrives for issue #X with active session:
Option A: Immediately interrupt and resume with new context
Option B: Queue the comment and inject at next checkpoint
Option C: Let current execution finish, then auto-resume with accumulated comments
Recommendation
Given the complexity, I recommend a phased approach:
Phase 1: Session State Tracking (Low Effort)
Store session IDs in queue.json after each job completes
When new event arrives for same issue, include "Previous session: X" in the prompt
Claude can use Gogs MCP tools to check previous comments anyway (already works!)
Phase 2: Resume-Based Injection (Medium Effort)
Store active session IDs in a sessions.json file
When new comment arrives, create a new job that uses resume: sessionId
Still sequential (no true mid-execution), but maintains full context
Phase 3: True Mid-Run Injection (High Effort)
Implement SessionManager daemon
Add IPC or REST API for session control
Support real-time interrupt + resume
Current Workaround
Good news: The current implementation already handles this reasonably well!
When a new comment is added to an issue:
A new job is created and queued
The job starts and Claude fetches ALL issue comments via Gogs MCP tools (see buildPrompt() in client/src/claude-client.ts)
Claude reads the full conversation history including new comments
Limitation: If Claude is currently running for that issue, the new comment won't be seen until:
Current execution completes
New job processes and Claude re-reads all comments
Question Back to You
Before I implement any changes, I need to understand your use case better:
How critical is real-time injection?
Is it okay if new comments are processed after the current task finishes (5-10 minutes delay)?
Or do you need instant mid-execution interruption?
What should happen to in-progress work?
Interrupt and restart with new context?
Complete current task, then process new input?
Try to merge new input into ongoing work?
How often do you expect mid-run updates?
Rare (once per issue)?
Common (multiple comments while Claude is working)?
Let me know your preference and I can design the appropriate solution! 🚀
## Analysis: Injecting Messages to Running Claude Agent
I've analyzed the current implementation and the Claude Agent SDK capabilities. Here's what I found:
---
### **Current Implementation**
The current system works as follows:
1. **Webhook Event → Job Queue**: When an issue is created/modified or a comment is added, the webhook server receives the event and adds it to a **persistent job queue** (`src/jobQueue.js`)
2. **Sequential Processing**: Jobs are processed **one at a time** (FIFO - first in, first out)
3. **Single Execution**: Each job creates a **new Claude Agent SDK session** that runs independently until completion
4. **No Session Sharing**: There's currently **no mechanism** to inject new information into an already-running Claude agent session
**Key Code Location**: `client/src/claude-client.ts` - The `handleIssue()` method creates a new query session for each job:
```typescript
const queryInstance = query({
prompt,
options: { cwd, mcpServers, allowedTools, ... }
});
for await (const message of queryInstance) {
// Process messages until completion
}
```
---
### **Is Mid-Run Injection Possible?**
**YES**, but it requires significant architecture changes. The Claude Agent SDK provides several capabilities that could enable this:
#### **1. Session Resume & Fork** ✅
- **What it does**: Allows resuming a previous session with new instructions
- **How**: Capture the `sessionId` from the initial response, then use the `resume` parameter:
```typescript
// Initial query
const response = query({ prompt: "Work on issue #20" });
for await (const message of response) {
if (message.type === 'system' && message.subtype === 'init') {
sessionId = message.session_id; // Save this!
}
}
// Later: Resume with new context
const resumed = query({
prompt: "New comment: User says X",
options: { resume: sessionId }
});
```
#### **2. Interrupt Method** ⚠️
- **What it does**: Stops a running agent mid-execution
- **How**: Call `await client.interrupt()` on the SDK client instance
- **Limitation**: Requires maintaining a reference to the client instance, which our current implementation doesn't do
#### **3. Fork Session** 🔀
- **What it does**: Creates a branch from an existing session without modifying the original
- **How**: Use `forkSession: true` with the `resume` parameter
- **Use Case**: Explore alternative approaches from the same starting point
---
### **Implementation Challenges**
To implement mid-run message injection, we would need to:
#### **Challenge 1: Long-Running Process Management**
- Current: Each job creates a **new Node.js process** via `handle-issue-ts.sh` → `client/dist/index.js`
- Problem: No way to communicate with an already-running process
- **Solution Options**:
- **Option A**: Convert to a **persistent daemon** that maintains active sessions
- **Option B**: Use **IPC (Inter-Process Communication)** to send messages to running processes
- **Option C**: Store session IDs and **queue resume operations** when new events arrive
#### **Challenge 2: Session State Persistence**
- Need to store `sessionId` for each running/paused issue
- Track which issues have active sessions
- Determine when to resume vs. fork vs. create new session
#### **Challenge 3: Queue Coordination**
- Current queue processes **one job at a time**
- Mid-run injection would need to:
- Detect if issue #X already has a running session
- Either interrupt it and resume with new context, OR
- Queue the new message for the next resume cycle
#### **Challenge 4: SDK API Limitations**
- The SDK `interrupt()` method requires holding a reference to the client instance
- Our current implementation doesn't expose the client between invocations
- Would need to refactor to maintain **persistent client instances**
---
### **Proposed Architecture (High-Level)**
If we want to support mid-run injection, here's a possible approach:
#### **Architecture: Session Manager Service**
```
┌─────────────────────────────────────────────────┐
│ Webhook Server (existing) │
│ - Receives events from Gogs │
│ - Enqueues jobs to JobQueue │
└──────────────────┬──────────────────────────────┘
│
v
┌─────────────────────────────────────────────────┐
│ Session Manager (NEW) │
│ - Maintains active Claude Agent sessions │
│ - Maps issue_number → sessionId │
│ - Handles: start, resume, interrupt, fork │
└──────────────────┬──────────────────────────────┘
│
v
┌─────────────────────────────────────────────────┐
│ Job Queue (modified) │
│ - Checks if session exists for issue │
│ - Routes to SessionManager for active sessions │
│ - Creates new session for new issues │
└─────────────────────────────────────────────────┘
```
#### **Key Components**
1. **SessionManager Class**
- Maintains a `Map<issueNumber, { sessionId, client, status }>`
- Provides: `startSession()`, `resumeSession()`, `interruptSession()`, `forkSession()`
- Persists session state to disk for crash recovery
2. **Modified JobQueue**
- Before creating new job, check if `SessionManager.hasActiveSession(issueNumber)`
- If yes: call `SessionManager.resumeSession(issueNumber, newPrompt)`
- If no: create new session via `SessionManager.startSession()`
3. **Event Deduplication**
- When new comment arrives for issue #X with active session:
- **Option A**: Immediately interrupt and resume with new context
- **Option B**: Queue the comment and inject at next checkpoint
- **Option C**: Let current execution finish, then auto-resume with accumulated comments
---
### **Recommendation**
Given the complexity, I recommend a **phased approach**:
#### **Phase 1: Session State Tracking (Low Effort)**
- Store session IDs in `queue.json` after each job completes
- When new event arrives for same issue, include "Previous session: X" in the prompt
- Claude can use Gogs MCP tools to check previous comments anyway (already works!)
#### **Phase 2: Resume-Based Injection (Medium Effort)**
- Store active session IDs in a `sessions.json` file
- When new comment arrives, create a new job that uses `resume: sessionId`
- Still sequential (no true mid-execution), but maintains full context
#### **Phase 3: True Mid-Run Injection (High Effort)**
- Implement SessionManager daemon
- Add IPC or REST API for session control
- Support real-time interrupt + resume
---
### **Current Workaround**
**Good news**: The current implementation already handles this reasonably well!
When a new comment is added to an issue:
1. A new job is created and queued
2. The job starts and Claude fetches **ALL** issue comments via Gogs MCP tools (see `buildPrompt()` in `client/src/claude-client.ts`)
3. Claude reads the full conversation history including new comments
**Limitation**: If Claude is currently running for that issue, the new comment won't be seen until:
- Current execution completes
- New job processes and Claude re-reads all comments
---
### **Question Back to You**
Before I implement any changes, I need to understand your use case better:
1. **How critical is real-time injection?**
- Is it okay if new comments are processed after the current task finishes (5-10 minutes delay)?
- Or do you need instant mid-execution interruption?
2. **What should happen to in-progress work?**
- Interrupt and restart with new context?
- Complete current task, then process new input?
- Try to merge new input into ongoing work?
3. **How often do you expect mid-run updates?**
- Rare (once per issue)?
- Common (multiple comments while Claude is working)?
Let me know your preference and I can design the appropriate solution! 🚀
So, if a new issue created, assigned to claude. Then calude agent will start to work on it. While claude agent is running, but user adds a new comment into the issue or modify the issue itself. Possible to inject the new message (messages) / issue changes to the running claude agent?
Analysis: Injecting Messages to Running Claude Agent
I've analyzed the current implementation and the Claude Agent SDK capabilities. Here's what I found:
Current Implementation
The current system works as follows:
src/jobQueue.js)Key Code Location:
client/src/claude-client.ts- ThehandleIssue()method creates a new query session for each job:Is Mid-Run Injection Possible?
YES, but it requires significant architecture changes. The Claude Agent SDK provides several capabilities that could enable this:
1. Session Resume & Fork ✅
How: Capture the
sessionIdfrom the initial response, then use theresumeparameter:2. Interrupt Method ⚠️
await client.interrupt()on the SDK client instance3. Fork Session 🔀
forkSession: truewith theresumeparameterImplementation Challenges
To implement mid-run message injection, we would need to:
Challenge 1: Long-Running Process Management
handle-issue-ts.sh→client/dist/index.jsChallenge 2: Session State Persistence
sessionIdfor each running/paused issueChallenge 3: Queue Coordination
Challenge 4: SDK API Limitations
interrupt()method requires holding a reference to the client instanceProposed Architecture (High-Level)
If we want to support mid-run injection, here's a possible approach:
Architecture: Session Manager Service
Key Components
SessionManager Class
Map<issueNumber, { sessionId, client, status }>startSession(),resumeSession(),interruptSession(),forkSession()Modified JobQueue
SessionManager.hasActiveSession(issueNumber)SessionManager.resumeSession(issueNumber, newPrompt)SessionManager.startSession()Event Deduplication
Recommendation
Given the complexity, I recommend a phased approach:
Phase 1: Session State Tracking (Low Effort)
queue.jsonafter each job completesPhase 2: Resume-Based Injection (Medium Effort)
sessions.jsonfileresume: sessionIdPhase 3: True Mid-Run Injection (High Effort)
Current Workaround
Good news: The current implementation already handles this reasonably well!
When a new comment is added to an issue:
buildPrompt()inclient/src/claude-client.ts)Limitation: If Claude is currently running for that issue, the new comment won't be seen until:
Question Back to You
Before I implement any changes, I need to understand your use case better:
How critical is real-time injection?
What should happen to in-progress work?
How often do you expect mid-run updates?
Let me know your preference and I can design the appropriate solution! 🚀